mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 13:11:40 +00:00
Merge branch 'next' into for-linus
This commit is contained in:
commit
93db628658
@ -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;
|
||||
|
||||
flush_scheduled_work();
|
||||
spin_lock(&driver_lock);
|
||||
file->private_data = NULL;
|
||||
del_singleshot_timer_sync(&chip->user_read_timer);
|
||||
flush_scheduled_work();
|
||||
file->private_data = NULL;
|
||||
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;
|
||||
}
|
||||
|
@ -13,7 +13,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
|
||||
@ -47,17 +47,7 @@
|
||||
#include <asm/bug.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
struct cipso_v4_domhsh_entry {
|
||||
char *domain;
|
||||
u32 valid;
|
||||
struct list_head list;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
/* List of available DOI definitions */
|
||||
/* XXX - Updates should be minimal so having a single lock for the
|
||||
* cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be
|
||||
* okay. */
|
||||
/* XXX - This currently assumes a minimal number of different DOIs in use,
|
||||
* if in practice there are a lot of different DOIs this list should
|
||||
* probably be turned into a hash table or something similar so we
|
||||
@ -119,6 +109,19 @@ int cipso_v4_rbm_strictvalid = 1;
|
||||
* be omitted. */
|
||||
#define CIPSO_V4_TAG_RNG_CAT_MAX 8
|
||||
|
||||
/* Base length of the local tag (non-standard tag).
|
||||
* Tag definition (may change between kernel versions)
|
||||
*
|
||||
* 0 8 16 24 32
|
||||
* +----------+----------+----------+----------+
|
||||
* | 10000000 | 00000110 | 32-bit secid value |
|
||||
* +----------+----------+----------+----------+
|
||||
* | in (host byte order)|
|
||||
* +----------+----------+
|
||||
*
|
||||
*/
|
||||
#define CIPSO_V4_TAG_LOC_BLEN 6
|
||||
|
||||
/*
|
||||
* Helper Functions
|
||||
*/
|
||||
@ -193,25 +196,6 @@ static void cipso_v4_bitmap_setbit(unsigned char *bitmap,
|
||||
bitmap[byte_spot] &= ~bitmask;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_doi_domhsh_free - Frees a domain list entry
|
||||
* @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 a domain list entry can be released
|
||||
* safely.
|
||||
*
|
||||
*/
|
||||
static void cipso_v4_doi_domhsh_free(struct rcu_head *entry)
|
||||
{
|
||||
struct cipso_v4_domhsh_entry *ptr;
|
||||
|
||||
ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu);
|
||||
kfree(ptr->domain);
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_cache_entry_free - Frees a cache entry
|
||||
* @entry: the entry to free
|
||||
@ -457,7 +441,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
|
||||
struct cipso_v4_doi *iter;
|
||||
|
||||
list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
|
||||
if (iter->doi == doi && iter->valid)
|
||||
if (iter->doi == doi && atomic_read(&iter->refcount))
|
||||
return iter;
|
||||
return NULL;
|
||||
}
|
||||
@ -496,14 +480,17 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
|
||||
if (doi_def->type != CIPSO_V4_MAP_PASS)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case CIPSO_V4_TAG_LOCAL:
|
||||
if (doi_def->type != CIPSO_V4_MAP_LOCAL)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
doi_def->valid = 1;
|
||||
atomic_set(&doi_def->refcount, 1);
|
||||
INIT_RCU_HEAD(&doi_def->rcu);
|
||||
INIT_LIST_HEAD(&doi_def->dom_list);
|
||||
|
||||
spin_lock(&cipso_v4_doi_list_lock);
|
||||
if (cipso_v4_doi_search(doi_def->doi) != NULL)
|
||||
@ -519,59 +506,129 @@ doi_add_failure:
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
|
||||
* @doi: the DOI value
|
||||
* @audit_secid: the LSM secid to use in the audit message
|
||||
* @callback: the DOI cleanup/free callback
|
||||
* cipso_v4_doi_free - Frees a DOI definition
|
||||
* @entry: the entry's RCU field
|
||||
*
|
||||
* Description:
|
||||
* Removes a DOI definition from the CIPSO engine, @callback is called to
|
||||
* free any memory. The NetLabel routines will be called to release their own
|
||||
* LSM domain mappings as well as our own domain list. Returns zero on
|
||||
* success and negative values on failure.
|
||||
* This function frees all of the memory associated with a DOI definition.
|
||||
*
|
||||
*/
|
||||
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)
|
||||
{
|
||||
struct cipso_v4_doi *doi_def;
|
||||
struct cipso_v4_domhsh_entry *dom_iter;
|
||||
if (doi_def == NULL)
|
||||
return;
|
||||
|
||||
spin_lock(&cipso_v4_doi_list_lock);
|
||||
doi_def = cipso_v4_doi_search(doi);
|
||||
if (doi_def != NULL) {
|
||||
doi_def->valid = 0;
|
||||
list_del_rcu(&doi_def->list);
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
|
||||
if (dom_iter->valid)
|
||||
netlbl_cfg_map_del(dom_iter->domain,
|
||||
audit_info);
|
||||
rcu_read_unlock();
|
||||
cipso_v4_cache_invalidate();
|
||||
call_rcu(&doi_def->rcu, callback);
|
||||
return 0;
|
||||
switch (doi_def->type) {
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
kfree(doi_def->map.std->lvl.cipso);
|
||||
kfree(doi_def->map.std->lvl.local);
|
||||
kfree(doi_def->map.std->cat.cipso);
|
||||
kfree(doi_def->map.std->cat.local);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
|
||||
return -ENOENT;
|
||||
kfree(doi_def);
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition
|
||||
* cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer
|
||||
* @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.
|
||||
*
|
||||
*/
|
||||
static void cipso_v4_doi_free_rcu(struct rcu_head *entry)
|
||||
{
|
||||
struct cipso_v4_doi *doi_def;
|
||||
|
||||
doi_def = container_of(entry, struct cipso_v4_doi, rcu);
|
||||
cipso_v4_doi_free(doi_def);
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
|
||||
* @doi: the DOI value
|
||||
* @audit_secid: the LSM secid to use in the audit message
|
||||
*
|
||||
* Description:
|
||||
* Removes a DOI definition from the CIPSO engine. The NetLabel routines will
|
||||
* be called to release their own LSM domain mappings as well as our own
|
||||
* domain list. Returns zero on success and negative values on failure.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
|
||||
{
|
||||
struct cipso_v4_doi *doi_def;
|
||||
|
||||
spin_lock(&cipso_v4_doi_list_lock);
|
||||
doi_def = cipso_v4_doi_search(doi);
|
||||
if (doi_def == NULL) {
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
if (!atomic_dec_and_test(&doi_def->refcount)) {
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
list_del_rcu(&doi_def->list);
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
|
||||
cipso_v4_cache_invalidate();
|
||||
call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_doi_getdef - Returns a reference to a valid DOI definition
|
||||
* @doi: the DOI value
|
||||
*
|
||||
* Description:
|
||||
* Searches for a valid DOI definition and if one is found it is returned to
|
||||
* the caller. Otherwise NULL is returned. The caller must ensure that
|
||||
* rcu_read_lock() is held while accessing the returned definition.
|
||||
* rcu_read_lock() is held while accessing the returned definition and the DOI
|
||||
* definition reference count is decremented when the caller is done.
|
||||
*
|
||||
*/
|
||||
struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
|
||||
{
|
||||
return cipso_v4_doi_search(doi);
|
||||
struct cipso_v4_doi *doi_def;
|
||||
|
||||
rcu_read_lock();
|
||||
doi_def = cipso_v4_doi_search(doi);
|
||||
if (doi_def == NULL)
|
||||
goto doi_getdef_return;
|
||||
if (!atomic_inc_not_zero(&doi_def->refcount))
|
||||
doi_def = NULL;
|
||||
|
||||
doi_getdef_return:
|
||||
rcu_read_unlock();
|
||||
return doi_def;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_doi_putdef - Releases a reference for the given DOI definition
|
||||
* @doi_def: the DOI definition
|
||||
*
|
||||
* Description:
|
||||
* Releases a DOI definition reference obtained from cipso_v4_doi_getdef().
|
||||
*
|
||||
*/
|
||||
void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def)
|
||||
{
|
||||
if (doi_def == NULL)
|
||||
return;
|
||||
|
||||
if (!atomic_dec_and_test(&doi_def->refcount))
|
||||
return;
|
||||
spin_lock(&cipso_v4_doi_list_lock);
|
||||
list_del_rcu(&doi_def->list);
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
|
||||
cipso_v4_cache_invalidate();
|
||||
call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -597,7 +654,7 @@ int cipso_v4_doi_walk(u32 *skip_cnt,
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
|
||||
if (iter_doi->valid) {
|
||||
if (atomic_read(&iter_doi->refcount) > 0) {
|
||||
if (doi_cnt++ < *skip_cnt)
|
||||
continue;
|
||||
ret_val = callback(iter_doi, cb_arg);
|
||||
@ -613,85 +670,6 @@ doi_walk_return:
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition
|
||||
* @doi_def: the DOI definition
|
||||
* @domain: the domain to add
|
||||
*
|
||||
* Description:
|
||||
* Adds the @domain to the DOI specified by @doi_def, this function
|
||||
* should only be called by external functions (i.e. NetLabel). This function
|
||||
* does allocate memory. Returns zero on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain)
|
||||
{
|
||||
struct cipso_v4_domhsh_entry *iter;
|
||||
struct cipso_v4_domhsh_entry *new_dom;
|
||||
|
||||
new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL);
|
||||
if (new_dom == NULL)
|
||||
return -ENOMEM;
|
||||
if (domain) {
|
||||
new_dom->domain = kstrdup(domain, GFP_KERNEL);
|
||||
if (new_dom->domain == NULL) {
|
||||
kfree(new_dom);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
new_dom->valid = 1;
|
||||
INIT_RCU_HEAD(&new_dom->rcu);
|
||||
|
||||
spin_lock(&cipso_v4_doi_list_lock);
|
||||
list_for_each_entry(iter, &doi_def->dom_list, list)
|
||||
if (iter->valid &&
|
||||
((domain != NULL && iter->domain != NULL &&
|
||||
strcmp(iter->domain, domain) == 0) ||
|
||||
(domain == NULL && iter->domain == NULL))) {
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
kfree(new_dom->domain);
|
||||
kfree(new_dom);
|
||||
return -EEXIST;
|
||||
}
|
||||
list_add_tail_rcu(&new_dom->list, &doi_def->dom_list);
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition
|
||||
* @doi_def: the DOI definition
|
||||
* @domain: the domain to remove
|
||||
*
|
||||
* Description:
|
||||
* Removes the @domain from the DOI specified by @doi_def, this function
|
||||
* should only be called by external functions (i.e. NetLabel). Returns zero
|
||||
* on success and negative values on error.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
|
||||
const char *domain)
|
||||
{
|
||||
struct cipso_v4_domhsh_entry *iter;
|
||||
|
||||
spin_lock(&cipso_v4_doi_list_lock);
|
||||
list_for_each_entry(iter, &doi_def->dom_list, list)
|
||||
if (iter->valid &&
|
||||
((domain != NULL && iter->domain != NULL &&
|
||||
strcmp(iter->domain, domain) == 0) ||
|
||||
(domain == NULL && iter->domain == NULL))) {
|
||||
iter->valid = 0;
|
||||
list_del_rcu(&iter->list);
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Label Mapping Functions
|
||||
*/
|
||||
@ -712,7 +690,7 @@ static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level)
|
||||
switch (doi_def->type) {
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
return 0;
|
||||
case CIPSO_V4_MAP_STD:
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL)
|
||||
return 0;
|
||||
break;
|
||||
@ -741,7 +719,7 @@ static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def,
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
*net_lvl = host_lvl;
|
||||
return 0;
|
||||
case CIPSO_V4_MAP_STD:
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
if (host_lvl < doi_def->map.std->lvl.local_size &&
|
||||
doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) {
|
||||
*net_lvl = doi_def->map.std->lvl.local[host_lvl];
|
||||
@ -775,7 +753,7 @@ static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def,
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
*host_lvl = net_lvl;
|
||||
return 0;
|
||||
case CIPSO_V4_MAP_STD:
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
map_tbl = doi_def->map.std;
|
||||
if (net_lvl < map_tbl->lvl.cipso_size &&
|
||||
map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) {
|
||||
@ -812,7 +790,7 @@ static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def,
|
||||
switch (doi_def->type) {
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
return 0;
|
||||
case CIPSO_V4_MAP_STD:
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
cipso_cat_size = doi_def->map.std->cat.cipso_size;
|
||||
cipso_array = doi_def->map.std->cat.cipso;
|
||||
for (;;) {
|
||||
@ -860,7 +838,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
|
||||
u32 host_cat_size = 0;
|
||||
u32 *host_cat_array = NULL;
|
||||
|
||||
if (doi_def->type == CIPSO_V4_MAP_STD) {
|
||||
if (doi_def->type == CIPSO_V4_MAP_TRANS) {
|
||||
host_cat_size = doi_def->map.std->cat.local_size;
|
||||
host_cat_array = doi_def->map.std->cat.local;
|
||||
}
|
||||
@ -875,7 +853,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
net_spot = host_spot;
|
||||
break;
|
||||
case CIPSO_V4_MAP_STD:
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
if (host_spot >= host_cat_size)
|
||||
return -EPERM;
|
||||
net_spot = host_cat_array[host_spot];
|
||||
@ -921,7 +899,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
|
||||
u32 net_cat_size = 0;
|
||||
u32 *net_cat_array = NULL;
|
||||
|
||||
if (doi_def->type == CIPSO_V4_MAP_STD) {
|
||||
if (doi_def->type == CIPSO_V4_MAP_TRANS) {
|
||||
net_cat_size = doi_def->map.std->cat.cipso_size;
|
||||
net_cat_array = doi_def->map.std->cat.cipso;
|
||||
}
|
||||
@ -941,7 +919,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
host_spot = net_spot;
|
||||
break;
|
||||
case CIPSO_V4_MAP_STD:
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
if (net_spot >= net_cat_size)
|
||||
return -EPERM;
|
||||
host_spot = net_cat_array[net_spot];
|
||||
@ -1277,7 +1255,7 @@ static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
|
||||
} else
|
||||
tag_len = 4;
|
||||
|
||||
buffer[0] = 0x01;
|
||||
buffer[0] = CIPSO_V4_TAG_RBITMAP;
|
||||
buffer[1] = tag_len;
|
||||
buffer[3] = level;
|
||||
|
||||
@ -1373,7 +1351,7 @@ static int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def,
|
||||
} else
|
||||
tag_len = 4;
|
||||
|
||||
buffer[0] = 0x02;
|
||||
buffer[0] = CIPSO_V4_TAG_ENUM;
|
||||
buffer[1] = tag_len;
|
||||
buffer[3] = level;
|
||||
|
||||
@ -1469,7 +1447,7 @@ static int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def,
|
||||
} else
|
||||
tag_len = 4;
|
||||
|
||||
buffer[0] = 0x05;
|
||||
buffer[0] = CIPSO_V4_TAG_RANGE;
|
||||
buffer[1] = tag_len;
|
||||
buffer[3] = level;
|
||||
|
||||
@ -1522,6 +1500,54 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_gentag_loc - Generate a CIPSO local tag (non-standard)
|
||||
* @doi_def: the DOI definition
|
||||
* @secattr: the security attributes
|
||||
* @buffer: the option buffer
|
||||
* @buffer_len: length of buffer in bytes
|
||||
*
|
||||
* Description:
|
||||
* Generate a CIPSO option using the local tag. Returns the size of the tag
|
||||
* on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
static int cipso_v4_gentag_loc(const struct cipso_v4_doi *doi_def,
|
||||
const struct netlbl_lsm_secattr *secattr,
|
||||
unsigned char *buffer,
|
||||
u32 buffer_len)
|
||||
{
|
||||
if (!(secattr->flags & NETLBL_SECATTR_SECID))
|
||||
return -EPERM;
|
||||
|
||||
buffer[0] = CIPSO_V4_TAG_LOCAL;
|
||||
buffer[1] = CIPSO_V4_TAG_LOC_BLEN;
|
||||
*(u32 *)&buffer[2] = secattr->attr.secid;
|
||||
|
||||
return CIPSO_V4_TAG_LOC_BLEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_parsetag_loc - Parse a CIPSO local tag
|
||||
* @doi_def: the DOI definition
|
||||
* @tag: the CIPSO tag
|
||||
* @secattr: the security attributes
|
||||
*
|
||||
* Description:
|
||||
* Parse a CIPSO local tag and return the security attributes in @secattr.
|
||||
* Return zero on success, negatives values on failure.
|
||||
*
|
||||
*/
|
||||
static int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def,
|
||||
const unsigned char *tag,
|
||||
struct netlbl_lsm_secattr *secattr)
|
||||
{
|
||||
secattr->attr.secid = *(u32 *)&tag[2];
|
||||
secattr->flags |= NETLBL_SECATTR_SECID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_validate - Validate a CIPSO option
|
||||
* @option: the start of the option, on error it is set to point to the error
|
||||
@ -1541,7 +1567,7 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
|
||||
* that is unrecognized."
|
||||
*
|
||||
*/
|
||||
int cipso_v4_validate(unsigned char **option)
|
||||
int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
|
||||
{
|
||||
unsigned char *opt = *option;
|
||||
unsigned char *tag;
|
||||
@ -1566,7 +1592,7 @@ int cipso_v4_validate(unsigned char **option)
|
||||
goto validate_return_locked;
|
||||
}
|
||||
|
||||
opt_iter = 6;
|
||||
opt_iter = CIPSO_V4_HDR_LEN;
|
||||
tag = opt + opt_iter;
|
||||
while (opt_iter < opt_len) {
|
||||
for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];)
|
||||
@ -1584,7 +1610,7 @@ int cipso_v4_validate(unsigned char **option)
|
||||
|
||||
switch (tag[0]) {
|
||||
case CIPSO_V4_TAG_RBITMAP:
|
||||
if (tag_len < 4) {
|
||||
if (tag_len < CIPSO_V4_TAG_RBM_BLEN) {
|
||||
err_offset = opt_iter + 1;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
@ -1602,7 +1628,7 @@ int cipso_v4_validate(unsigned char **option)
|
||||
err_offset = opt_iter + 3;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
if (tag_len > 4 &&
|
||||
if (tag_len > CIPSO_V4_TAG_RBM_BLEN &&
|
||||
cipso_v4_map_cat_rbm_valid(doi_def,
|
||||
&tag[4],
|
||||
tag_len - 4) < 0) {
|
||||
@ -1612,7 +1638,7 @@ int cipso_v4_validate(unsigned char **option)
|
||||
}
|
||||
break;
|
||||
case CIPSO_V4_TAG_ENUM:
|
||||
if (tag_len < 4) {
|
||||
if (tag_len < CIPSO_V4_TAG_ENUM_BLEN) {
|
||||
err_offset = opt_iter + 1;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
@ -1622,7 +1648,7 @@ int cipso_v4_validate(unsigned char **option)
|
||||
err_offset = opt_iter + 3;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
if (tag_len > 4 &&
|
||||
if (tag_len > CIPSO_V4_TAG_ENUM_BLEN &&
|
||||
cipso_v4_map_cat_enum_valid(doi_def,
|
||||
&tag[4],
|
||||
tag_len - 4) < 0) {
|
||||
@ -1631,7 +1657,7 @@ int cipso_v4_validate(unsigned char **option)
|
||||
}
|
||||
break;
|
||||
case CIPSO_V4_TAG_RANGE:
|
||||
if (tag_len < 4) {
|
||||
if (tag_len < CIPSO_V4_TAG_RNG_BLEN) {
|
||||
err_offset = opt_iter + 1;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
@ -1641,7 +1667,7 @@ int cipso_v4_validate(unsigned char **option)
|
||||
err_offset = opt_iter + 3;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
if (tag_len > 4 &&
|
||||
if (tag_len > CIPSO_V4_TAG_RNG_BLEN &&
|
||||
cipso_v4_map_cat_rng_valid(doi_def,
|
||||
&tag[4],
|
||||
tag_len - 4) < 0) {
|
||||
@ -1649,6 +1675,19 @@ int cipso_v4_validate(unsigned char **option)
|
||||
goto validate_return_locked;
|
||||
}
|
||||
break;
|
||||
case CIPSO_V4_TAG_LOCAL:
|
||||
/* This is a non-standard tag that we only allow for
|
||||
* local connections, so if the incoming interface is
|
||||
* not the loopback device drop the packet. */
|
||||
if (!(skb->dev->flags & IFF_LOOPBACK)) {
|
||||
err_offset = opt_iter;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
if (tag_len != CIPSO_V4_TAG_LOC_BLEN) {
|
||||
err_offset = opt_iter + 1;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
err_offset = opt_iter;
|
||||
goto validate_return_locked;
|
||||
@ -1704,48 +1743,27 @@ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_sock_setattr - Add a CIPSO option to a socket
|
||||
* @sk: the socket
|
||||
* cipso_v4_genopt - Generate a CIPSO option
|
||||
* @buf: the option buffer
|
||||
* @buf_len: the size of opt_buf
|
||||
* @doi_def: the CIPSO DOI to use
|
||||
* @secattr: the specific security attributes of the socket
|
||||
* @secattr: the security attributes
|
||||
*
|
||||
* Description:
|
||||
* Set the CIPSO option on the given socket using the DOI definition and
|
||||
* security attributes passed to the function. 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 and negative
|
||||
* values on failure.
|
||||
* Generate a CIPSO option using the DOI definition and security attributes
|
||||
* passed to the function. Returns the length of the option on success and
|
||||
* negative values on failure.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_sock_setattr(struct sock *sk,
|
||||
const struct cipso_v4_doi *doi_def,
|
||||
const struct netlbl_lsm_secattr *secattr)
|
||||
static int cipso_v4_genopt(unsigned char *buf, u32 buf_len,
|
||||
const struct cipso_v4_doi *doi_def,
|
||||
const struct netlbl_lsm_secattr *secattr)
|
||||
{
|
||||
int ret_val = -EPERM;
|
||||
int ret_val;
|
||||
u32 iter;
|
||||
unsigned char *buf;
|
||||
u32 buf_len = 0;
|
||||
u32 opt_len;
|
||||
struct ip_options *opt = NULL;
|
||||
struct inet_sock *sk_inet;
|
||||
struct inet_connection_sock *sk_conn;
|
||||
|
||||
/* In the case of sock_create_lite(), the sock->sk field is not
|
||||
* defined yet but it is not a problem as the only users of these
|
||||
* "lite" PF_INET sockets are functions which do an accept() call
|
||||
* afterwards so we will label the socket as part of the accept(). */
|
||||
if (sk == NULL)
|
||||
return 0;
|
||||
|
||||
/* We allocate the maximum CIPSO option size here so we are probably
|
||||
* being a little wasteful, but it makes our life _much_ easier later
|
||||
* on and after all we are only talking about 40 bytes. */
|
||||
buf_len = CIPSO_V4_OPT_LEN_MAX;
|
||||
buf = kmalloc(buf_len, GFP_ATOMIC);
|
||||
if (buf == NULL) {
|
||||
ret_val = -ENOMEM;
|
||||
goto socket_setattr_failure;
|
||||
}
|
||||
if (buf_len <= CIPSO_V4_HDR_LEN)
|
||||
return -ENOSPC;
|
||||
|
||||
/* XXX - This code assumes only one tag per CIPSO option which isn't
|
||||
* really a good assumption to make but since we only support the MAC
|
||||
@ -1772,9 +1790,14 @@ int cipso_v4_sock_setattr(struct sock *sk,
|
||||
&buf[CIPSO_V4_HDR_LEN],
|
||||
buf_len - CIPSO_V4_HDR_LEN);
|
||||
break;
|
||||
case CIPSO_V4_TAG_LOCAL:
|
||||
ret_val = cipso_v4_gentag_loc(doi_def,
|
||||
secattr,
|
||||
&buf[CIPSO_V4_HDR_LEN],
|
||||
buf_len - CIPSO_V4_HDR_LEN);
|
||||
break;
|
||||
default:
|
||||
ret_val = -EPERM;
|
||||
goto socket_setattr_failure;
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
iter++;
|
||||
@ -1782,9 +1805,58 @@ int cipso_v4_sock_setattr(struct sock *sk,
|
||||
iter < CIPSO_V4_TAG_MAXCNT &&
|
||||
doi_def->tags[iter] != CIPSO_V4_TAG_INVALID);
|
||||
if (ret_val < 0)
|
||||
goto socket_setattr_failure;
|
||||
return ret_val;
|
||||
cipso_v4_gentag_hdr(doi_def, buf, ret_val);
|
||||
buf_len = CIPSO_V4_HDR_LEN + ret_val;
|
||||
return CIPSO_V4_HDR_LEN + ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_sock_setattr - Add a CIPSO option to a socket
|
||||
* @sk: the socket
|
||||
* @doi_def: the CIPSO DOI to use
|
||||
* @secattr: the specific security attributes of the socket
|
||||
*
|
||||
* Description:
|
||||
* Set the CIPSO option on the given socket using the DOI definition and
|
||||
* security attributes passed to the function. 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 and negative
|
||||
* values on failure.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_sock_setattr(struct sock *sk,
|
||||
const struct cipso_v4_doi *doi_def,
|
||||
const struct netlbl_lsm_secattr *secattr)
|
||||
{
|
||||
int ret_val = -EPERM;
|
||||
unsigned char *buf = NULL;
|
||||
u32 buf_len;
|
||||
u32 opt_len;
|
||||
struct ip_options *opt = NULL;
|
||||
struct inet_sock *sk_inet;
|
||||
struct inet_connection_sock *sk_conn;
|
||||
|
||||
/* In the case of sock_create_lite(), the sock->sk field is not
|
||||
* defined yet but it is not a problem as the only users of these
|
||||
* "lite" PF_INET sockets are functions which do an accept() call
|
||||
* afterwards so we will label the socket as part of the accept(). */
|
||||
if (sk == NULL)
|
||||
return 0;
|
||||
|
||||
/* We allocate the maximum CIPSO option size here so we are probably
|
||||
* being a little wasteful, but it makes our life _much_ easier later
|
||||
* on and after all we are only talking about 40 bytes. */
|
||||
buf_len = CIPSO_V4_OPT_LEN_MAX;
|
||||
buf = kmalloc(buf_len, GFP_ATOMIC);
|
||||
if (buf == NULL) {
|
||||
ret_val = -ENOMEM;
|
||||
goto socket_setattr_failure;
|
||||
}
|
||||
|
||||
ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
|
||||
if (ret_val < 0)
|
||||
goto socket_setattr_failure;
|
||||
buf_len = ret_val;
|
||||
|
||||
/* We can't use ip_options_get() directly because it makes a call to
|
||||
* ip_options_get_alloc() which allocates memory with GFP_KERNEL and
|
||||
@ -1821,6 +1893,80 @@ socket_setattr_failure:
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_sock_delattr - Delete the CIPSO option from a socket
|
||||
* @sk: the socket
|
||||
*
|
||||
* Description:
|
||||
* Removes the CIPSO option from a socket, if present.
|
||||
*
|
||||
*/
|
||||
void cipso_v4_sock_delattr(struct sock *sk)
|
||||
{
|
||||
u8 hdr_delta;
|
||||
struct ip_options *opt;
|
||||
struct inet_sock *sk_inet;
|
||||
|
||||
sk_inet = inet_sk(sk);
|
||||
opt = sk_inet->opt;
|
||||
if (opt == NULL || opt->cipso == 0)
|
||||
return;
|
||||
|
||||
if (opt->srr || opt->rr || opt->ts || opt->router_alert) {
|
||||
u8 cipso_len;
|
||||
u8 cipso_off;
|
||||
unsigned char *cipso_ptr;
|
||||
int iter;
|
||||
int optlen_new;
|
||||
|
||||
cipso_off = opt->cipso - sizeof(struct iphdr);
|
||||
cipso_ptr = &opt->__data[cipso_off];
|
||||
cipso_len = cipso_ptr[1];
|
||||
|
||||
if (opt->srr > opt->cipso)
|
||||
opt->srr -= cipso_len;
|
||||
if (opt->rr > opt->cipso)
|
||||
opt->rr -= cipso_len;
|
||||
if (opt->ts > opt->cipso)
|
||||
opt->ts -= cipso_len;
|
||||
if (opt->router_alert > opt->cipso)
|
||||
opt->router_alert -= cipso_len;
|
||||
opt->cipso = 0;
|
||||
|
||||
memmove(cipso_ptr, cipso_ptr + cipso_len,
|
||||
opt->optlen - cipso_off - cipso_len);
|
||||
|
||||
/* determining the new total option length is tricky because of
|
||||
* the padding necessary, the only thing i can think to do at
|
||||
* this point is walk the options one-by-one, skipping the
|
||||
* padding at the end to determine the actual option size and
|
||||
* from there we can determine the new total option length */
|
||||
iter = 0;
|
||||
optlen_new = 0;
|
||||
while (iter < opt->optlen)
|
||||
if (opt->__data[iter] != IPOPT_NOP) {
|
||||
iter += opt->__data[iter + 1];
|
||||
optlen_new = iter;
|
||||
} else
|
||||
iter++;
|
||||
hdr_delta = opt->optlen;
|
||||
opt->optlen = (optlen_new + 3) & ~3;
|
||||
hdr_delta -= opt->optlen;
|
||||
} else {
|
||||
/* only the cipso option was present on the socket so we can
|
||||
* remove the entire option struct */
|
||||
sk_inet->opt = NULL;
|
||||
hdr_delta = opt->optlen;
|
||||
kfree(opt);
|
||||
}
|
||||
|
||||
if (sk_inet->is_icsk && hdr_delta > 0) {
|
||||
struct inet_connection_sock *sk_conn = inet_csk(sk);
|
||||
sk_conn->icsk_ext_hdr_len -= hdr_delta;
|
||||
sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions
|
||||
* @cipso: the CIPSO v4 option
|
||||
@ -1859,6 +2005,9 @@ static int cipso_v4_getattr(const unsigned char *cipso,
|
||||
case CIPSO_V4_TAG_RANGE:
|
||||
ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr);
|
||||
break;
|
||||
case CIPSO_V4_TAG_LOCAL:
|
||||
ret_val = cipso_v4_parsetag_loc(doi_def, &cipso[6], secattr);
|
||||
break;
|
||||
}
|
||||
if (ret_val == 0)
|
||||
secattr->type = NETLBL_NLTYPE_CIPSOV4;
|
||||
@ -1892,6 +2041,123 @@ int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
|
||||
secattr);
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_skbuff_setattr - Set the CIPSO option on a packet
|
||||
* @skb: the packet
|
||||
* @secattr: the security attributes
|
||||
*
|
||||
* Description:
|
||||
* Set the CIPSO option on the given packet based on the security attributes.
|
||||
* Returns a pointer to the IP header on success and NULL on failure.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_skbuff_setattr(struct sk_buff *skb,
|
||||
const struct cipso_v4_doi *doi_def,
|
||||
const struct netlbl_lsm_secattr *secattr)
|
||||
{
|
||||
int ret_val;
|
||||
struct iphdr *iph;
|
||||
struct ip_options *opt = &IPCB(skb)->opt;
|
||||
unsigned char buf[CIPSO_V4_OPT_LEN_MAX];
|
||||
u32 buf_len = CIPSO_V4_OPT_LEN_MAX;
|
||||
u32 opt_len;
|
||||
int len_delta;
|
||||
|
||||
buf_len = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
|
||||
if (buf_len < 0)
|
||||
return buf_len;
|
||||
opt_len = (buf_len + 3) & ~3;
|
||||
|
||||
/* we overwrite any existing options to ensure that we have enough
|
||||
* room for the CIPSO option, the reason is that we _need_ to guarantee
|
||||
* that the security label is applied to the packet - we do the same
|
||||
* thing when using the socket options and it hasn't caused a problem,
|
||||
* if we need to we can always revisit this choice later */
|
||||
|
||||
len_delta = opt_len - opt->optlen;
|
||||
/* if we don't ensure enough headroom we could panic on the skb_push()
|
||||
* call below so make sure we have enough, we are also "mangling" the
|
||||
* packet so we should probably do a copy-on-write call anyway */
|
||||
ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
|
||||
if (ret_val < 0)
|
||||
return ret_val;
|
||||
|
||||
if (len_delta > 0) {
|
||||
/* we assume that the header + opt->optlen have already been
|
||||
* "pushed" in ip_options_build() or similar */
|
||||
iph = ip_hdr(skb);
|
||||
skb_push(skb, len_delta);
|
||||
memmove((char *)iph - len_delta, iph, iph->ihl << 2);
|
||||
skb_reset_network_header(skb);
|
||||
iph = ip_hdr(skb);
|
||||
} else if (len_delta < 0) {
|
||||
iph = ip_hdr(skb);
|
||||
memset(iph + 1, IPOPT_NOP, opt->optlen);
|
||||
} else
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
if (opt->optlen > 0)
|
||||
memset(opt, 0, sizeof(*opt));
|
||||
opt->optlen = opt_len;
|
||||
opt->cipso = sizeof(struct iphdr);
|
||||
opt->is_changed = 1;
|
||||
|
||||
/* we have to do the following because we are being called from a
|
||||
* netfilter hook which means the packet already has had the header
|
||||
* fields populated and the checksum calculated - yes this means we
|
||||
* are doing more work than needed but we do it to keep the core
|
||||
* stack clean and tidy */
|
||||
memcpy(iph + 1, buf, buf_len);
|
||||
if (opt_len > buf_len)
|
||||
memset((char *)(iph + 1) + buf_len, 0, opt_len - buf_len);
|
||||
if (len_delta != 0) {
|
||||
iph->ihl = 5 + (opt_len >> 2);
|
||||
iph->tot_len = htons(skb->len);
|
||||
}
|
||||
ip_send_check(iph);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_skbuff_delattr - Delete any CIPSO options from a packet
|
||||
* @skb: the packet
|
||||
*
|
||||
* Description:
|
||||
* Removes any and all CIPSO options from the given packet. Returns zero on
|
||||
* success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_skbuff_delattr(struct sk_buff *skb)
|
||||
{
|
||||
int ret_val;
|
||||
struct iphdr *iph;
|
||||
struct ip_options *opt = &IPCB(skb)->opt;
|
||||
unsigned char *cipso_ptr;
|
||||
|
||||
if (opt->cipso == 0)
|
||||
return 0;
|
||||
|
||||
/* since we are changing the packet we should make a copy */
|
||||
ret_val = skb_cow(skb, skb_headroom(skb));
|
||||
if (ret_val < 0)
|
||||
return ret_val;
|
||||
|
||||
/* the easiest thing to do is just replace the cipso option with noop
|
||||
* options since we don't change the size of the packet, although we
|
||||
* still need to recalculate the checksum */
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
cipso_ptr = (unsigned char *)iph + opt->cipso;
|
||||
memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]);
|
||||
opt->cipso = 0;
|
||||
opt->is_changed = 1;
|
||||
|
||||
ip_send_check(iph);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option
|
||||
* @skb: the packet
|
||||
|
@ -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
|
||||
|
388
net/netlabel/netlabel_addrlist.c
Normal file
388
net/netlabel/netlabel_addrlist.c
Normal file
@ -0,0 +1,388 @@
|
||||
/*
|
||||
* 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 */
|
189
net/netlabel/netlabel_addrlist.h
Normal file
189
net/netlabel/netlabel_addrlist.h
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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 @@ list_start:
|
||||
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 @@ list_start:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
@ -301,6 +417,71 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
|
||||
return netlbl_domhsh_add(entry, audit_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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). Caller is responsible for
|
||||
* ensuring that the RCU read lock is held. Returns zero on success, negative
|
||||
* on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct audit_buffer *audit_buf;
|
||||
|
||||
if (entry == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
spin_lock(&netlbl_domhsh_lock);
|
||||
if (entry->valid) {
|
||||
entry->valid = 0;
|
||||
if (entry != rcu_dereference(netlbl_domhsh_def))
|
||||
list_del_rcu(&entry->list);
|
||||
else
|
||||
rcu_assign_pointer(netlbl_domhsh_def, NULL);
|
||||
} else
|
||||
ret_val = -ENOENT;
|
||||
spin_unlock(&netlbl_domhsh_lock);
|
||||
|
||||
audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
audit_log_format(audit_buf,
|
||||
" nlbl_domain=%s res=%u",
|
||||
entry->domain ? entry->domain : "(default)",
|
||||
ret_val == 0 ? 1 : 0);
|
||||
audit_log_end(audit_buf);
|
||||
}
|
||||
|
||||
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
|
||||
@ -314,47 +495,17 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
|
||||
*/
|
||||
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = -ENOENT;
|
||||
int ret_val;
|
||||
struct netlbl_dom_map *entry;
|
||||
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;
|
||||
}
|
||||
spin_lock(&netlbl_domhsh_lock);
|
||||
if (entry->valid) {
|
||||
entry->valid = 0;
|
||||
if (entry != rcu_dereference(netlbl_domhsh_def))
|
||||
list_del_rcu(&entry->list);
|
||||
else
|
||||
rcu_assign_pointer(netlbl_domhsh_def, NULL);
|
||||
ret_val = 0;
|
||||
}
|
||||
spin_unlock(&netlbl_domhsh_lock);
|
||||
|
||||
audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
audit_log_format(audit_buf,
|
||||
" nlbl_domain=%s res=%u",
|
||||
entry->domain ? entry->domain : "(default)",
|
||||
ret_val == 0 ? 1 : 0);
|
||||
audit_log_end(audit_buf);
|
||||
}
|
||||
|
||||
remove_return:
|
||||
ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
|
||||
rcu_read_unlock();
|
||||
if (ret_val == 0)
|
||||
call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
|
||||
|
||||
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 @@ cfg_unlbl_add_map_failure:
|
||||
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);
|
||||
if (ret_val != 0)
|
||||
goto cfg_cipsov4_add_map_failure_unlock;
|
||||
ret_val = netlbl_domhsh_add(entry, audit_info);
|
||||
ret_val = cipso_v4_doi_add(doi_def);
|
||||
if (ret_val != 0)
|
||||
goto cfg_cipsov4_add_map_failure_remove_doi;
|
||||
rcu_read_unlock();
|
||||
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_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 @@ socket_setattr_return:
|
||||
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>
|
||||
@ -70,6 +74,301 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
|
||||
[NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper Functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* netlbl_mgmt_add - Handle an ADD message
|
||||
* @info: the Generic NETLINK info block
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* 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_common(struct genl_info *info,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = -EINVAL;
|
||||
struct netlbl_dom_map *entry = NULL;
|
||||
struct netlbl_domaddr_map *addrmap = NULL;
|
||||
struct cipso_v4_doi *cipsov4 = NULL;
|
||||
u32 tmp_val;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (entry == NULL) {
|
||||
ret_val = -ENOMEM;
|
||||
goto add_failure;
|
||||
}
|
||||
entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
|
||||
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:
|
||||
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]);
|
||||
cipsov4 = cipso_v4_doi_getdef(tmp_val);
|
||||
if (cipsov4 == NULL)
|
||||
goto add_failure;
|
||||
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
|
||||
*/
|
||||
@ -87,67 +386,23 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
|
||||
*/
|
||||
static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int ret_val = -EINVAL;
|
||||
struct netlbl_dom_map *entry = NULL;
|
||||
size_t tmp_size;
|
||||
u32 tmp_val;
|
||||
struct netlbl_audit audit_info;
|
||||
|
||||
if (!info->attrs[NLBL_MGMT_A_DOMAIN] ||
|
||||
!info->attrs[NLBL_MGMT_A_PROTOCOL])
|
||||
goto add_failure;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
goto add_failure;
|
||||
}
|
||||
ret_val = netlbl_domhsh_add(entry, &audit_info);
|
||||
rcu_read_unlock();
|
||||
break;
|
||||
default:
|
||||
goto add_failure;
|
||||
}
|
||||
if (ret_val != 0)
|
||||
goto add_failure;
|
||||
|
||||
return 0;
|
||||
|
||||
add_failure:
|
||||
if (entry)
|
||||
kfree(entry->domain);
|
||||
kfree(entry);
|
||||
return ret_val;
|
||||
return netlbl_mgmt_add_common(info, &audit_info);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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)
|
||||
|
@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2007
|
||||
* (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
|
||||
@ -54,6 +54,7 @@
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include "netlabel_user.h"
|
||||
#include "netlabel_addrlist.h"
|
||||
#include "netlabel_domainhash.h"
|
||||
#include "netlabel_unlabeled.h"
|
||||
#include "netlabel_mgmt.h"
|
||||
@ -76,22 +77,20 @@ struct netlbl_unlhsh_tbl {
|
||||
struct list_head *tbl;
|
||||
u32 size;
|
||||
};
|
||||
#define netlbl_unlhsh_addr4_entry(iter) \
|
||||
container_of(iter, struct netlbl_unlhsh_addr4, list)
|
||||
struct netlbl_unlhsh_addr4 {
|
||||
__be32 addr;
|
||||
__be32 mask;
|
||||
u32 secid;
|
||||
|
||||
u32 valid;
|
||||
struct list_head list;
|
||||
struct netlbl_af4list list;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
#define netlbl_unlhsh_addr6_entry(iter) \
|
||||
container_of(iter, struct netlbl_unlhsh_addr6, list)
|
||||
struct netlbl_unlhsh_addr6 {
|
||||
struct in6_addr addr;
|
||||
struct in6_addr mask;
|
||||
u32 secid;
|
||||
|
||||
u32 valid;
|
||||
struct list_head list;
|
||||
struct netlbl_af6list list;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
struct netlbl_unlhsh_iface {
|
||||
@ -146,76 +145,6 @@ static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1
|
||||
[NLBL_UNLABEL_A_SECCTX] = { .type = NLA_BINARY }
|
||||
};
|
||||
|
||||
/*
|
||||
* Audit Helper Functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* netlbl_unlabel_audit_addr4 - Audit an IPv4 address
|
||||
* @audit_buf: audit buffer
|
||||
* @dev: network interface
|
||||
* @addr: IP address
|
||||
* @mask: IP address mask
|
||||
*
|
||||
* Description:
|
||||
* Write the IPv4 address and address mask, if necessary, to @audit_buf.
|
||||
*
|
||||
*/
|
||||
static void netlbl_unlabel_audit_addr4(struct audit_buffer *audit_buf,
|
||||
const char *dev,
|
||||
__be32 addr, __be32 mask)
|
||||
{
|
||||
u32 mask_val = ntohl(mask);
|
||||
|
||||
if (dev != NULL)
|
||||
audit_log_format(audit_buf, " netif=%s", dev);
|
||||
audit_log_format(audit_buf, " src=" NIPQUAD_FMT, NIPQUAD(addr));
|
||||
if (mask_val != 0xffffffff) {
|
||||
u32 mask_len = 0;
|
||||
while (mask_val > 0) {
|
||||
mask_val <<= 1;
|
||||
mask_len++;
|
||||
}
|
||||
audit_log_format(audit_buf, " src_prefixlen=%d", mask_len);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
/**
|
||||
* netlbl_unlabel_audit_addr6 - Audit an IPv6 address
|
||||
* @audit_buf: audit buffer
|
||||
* @dev: network interface
|
||||
* @addr: IP address
|
||||
* @mask: IP address mask
|
||||
*
|
||||
* Description:
|
||||
* Write the IPv6 address and address mask, if necessary, to @audit_buf.
|
||||
*
|
||||
*/
|
||||
static void netlbl_unlabel_audit_addr6(struct audit_buffer *audit_buf,
|
||||
const char *dev,
|
||||
const struct in6_addr *addr,
|
||||
const struct in6_addr *mask)
|
||||
{
|
||||
if (dev != NULL)
|
||||
audit_log_format(audit_buf, " netif=%s", dev);
|
||||
audit_log_format(audit_buf, " src=" NIP6_FMT, NIP6(*addr));
|
||||
if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {
|
||||
u32 mask_len = 0;
|
||||
u32 mask_val;
|
||||
int iter = -1;
|
||||
while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff)
|
||||
mask_len += 32;
|
||||
mask_val = ntohl(mask->s6_addr32[iter]);
|
||||
while (mask_val > 0) {
|
||||
mask_val <<= 1;
|
||||
mask_len++;
|
||||
}
|
||||
audit_log_format(audit_buf, " src_prefixlen=%d", mask_len);
|
||||
}
|
||||
}
|
||||
#endif /* IPv6 */
|
||||
|
||||
/*
|
||||
* Unlabeled Connection Hash Table Functions
|
||||
*/
|
||||
@ -274,26 +203,28 @@ static void netlbl_unlhsh_free_addr6(struct rcu_head *entry)
|
||||
static void netlbl_unlhsh_free_iface(struct rcu_head *entry)
|
||||
{
|
||||
struct netlbl_unlhsh_iface *iface;
|
||||
struct netlbl_unlhsh_addr4 *iter4;
|
||||
struct netlbl_unlhsh_addr4 *tmp4;
|
||||
struct netlbl_unlhsh_addr6 *iter6;
|
||||
struct netlbl_unlhsh_addr6 *tmp6;
|
||||
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 */
|
||||
|
||||
iface = container_of(entry, struct netlbl_unlhsh_iface, rcu);
|
||||
|
||||
/* no need for locks here since we are the only one with access to this
|
||||
* structure */
|
||||
|
||||
list_for_each_entry_safe(iter4, tmp4, &iface->addr4_list, list)
|
||||
if (iter4->valid) {
|
||||
list_del_rcu(&iter4->list);
|
||||
kfree(iter4);
|
||||
}
|
||||
list_for_each_entry_safe(iter6, tmp6, &iface->addr6_list, list)
|
||||
if (iter6->valid) {
|
||||
list_del_rcu(&iter6->list);
|
||||
kfree(iter6);
|
||||
}
|
||||
netlbl_af4list_foreach_safe(iter4, tmp4, &iface->addr4_list) {
|
||||
netlbl_af4list_remove_entry(iter4);
|
||||
kfree(netlbl_unlhsh_addr4_entry(iter4));
|
||||
}
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
netlbl_af6list_foreach_safe(iter6, tmp6, &iface->addr6_list) {
|
||||
netlbl_af6list_remove_entry(iter6);
|
||||
kfree(netlbl_unlhsh_addr6_entry(iter6));
|
||||
}
|
||||
#endif /* IPv6 */
|
||||
kfree(iface);
|
||||
}
|
||||
|
||||
@ -315,59 +246,6 @@ static u32 netlbl_unlhsh_hash(int ifindex)
|
||||
return ifindex & (rcu_dereference(netlbl_unlhsh)->size - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_unlhsh_search_addr4 - Search for a matching IPv4 address entry
|
||||
* @addr: IPv4 address
|
||||
* @iface: the network interface entry
|
||||
*
|
||||
* Description:
|
||||
* Searches the IPv4 address list of the network interface specified by @iface.
|
||||
* If a matching address entry is found it is returned, otherwise NULL is
|
||||
* returned. The caller is responsible for calling the rcu_read_[un]lock()
|
||||
* functions.
|
||||
*
|
||||
*/
|
||||
static struct netlbl_unlhsh_addr4 *netlbl_unlhsh_search_addr4(
|
||||
__be32 addr,
|
||||
const struct netlbl_unlhsh_iface *iface)
|
||||
{
|
||||
struct netlbl_unlhsh_addr4 *iter;
|
||||
|
||||
list_for_each_entry_rcu(iter, &iface->addr4_list, list)
|
||||
if (iter->valid && (addr & iter->mask) == iter->addr)
|
||||
return iter;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
/**
|
||||
* netlbl_unlhsh_search_addr6 - Search for a matching IPv6 address entry
|
||||
* @addr: IPv6 address
|
||||
* @iface: the network interface entry
|
||||
*
|
||||
* Description:
|
||||
* Searches the IPv6 address list of the network interface specified by @iface.
|
||||
* If a matching address entry is found it is returned, otherwise NULL is
|
||||
* returned. The caller is responsible for calling the rcu_read_[un]lock()
|
||||
* functions.
|
||||
*
|
||||
*/
|
||||
static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6(
|
||||
const struct in6_addr *addr,
|
||||
const struct netlbl_unlhsh_iface *iface)
|
||||
{
|
||||
struct netlbl_unlhsh_addr6 *iter;
|
||||
|
||||
list_for_each_entry_rcu(iter, &iface->addr6_list, list)
|
||||
if (iter->valid &&
|
||||
ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0)
|
||||
return iter;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif /* IPv6 */
|
||||
|
||||
/**
|
||||
* netlbl_unlhsh_search_iface - Search for a matching interface entry
|
||||
* @ifindex: the network interface
|
||||
@ -381,12 +259,12 @@ static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6(
|
||||
static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
|
||||
{
|
||||
u32 bkt;
|
||||
struct list_head *bkt_list;
|
||||
struct netlbl_unlhsh_iface *iter;
|
||||
|
||||
bkt = netlbl_unlhsh_hash(ifindex);
|
||||
list_for_each_entry_rcu(iter,
|
||||
&rcu_dereference(netlbl_unlhsh)->tbl[bkt],
|
||||
list)
|
||||
bkt_list = &rcu_dereference(netlbl_unlhsh)->tbl[bkt];
|
||||
list_for_each_entry_rcu(iter, bkt_list, list)
|
||||
if (iter->valid && iter->ifindex == ifindex)
|
||||
return iter;
|
||||
|
||||
@ -439,43 +317,26 @@ static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
|
||||
const struct in_addr *mask,
|
||||
u32 secid)
|
||||
{
|
||||
int ret_val;
|
||||
struct netlbl_unlhsh_addr4 *entry;
|
||||
struct netlbl_unlhsh_addr4 *iter;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||
if (entry == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
entry->addr = addr->s_addr & mask->s_addr;
|
||||
entry->mask = mask->s_addr;
|
||||
entry->secid = secid;
|
||||
entry->valid = 1;
|
||||
entry->list.addr = addr->s_addr & mask->s_addr;
|
||||
entry->list.mask = mask->s_addr;
|
||||
entry->list.valid = 1;
|
||||
INIT_RCU_HEAD(&entry->rcu);
|
||||
entry->secid = secid;
|
||||
|
||||
spin_lock(&netlbl_unlhsh_lock);
|
||||
iter = netlbl_unlhsh_search_addr4(entry->addr, iface);
|
||||
if (iter != NULL &&
|
||||
iter->addr == addr->s_addr && iter->mask == mask->s_addr) {
|
||||
spin_unlock(&netlbl_unlhsh_lock);
|
||||
kfree(entry);
|
||||
return -EEXIST;
|
||||
}
|
||||
/* in order to speed up address searches through the list (the common
|
||||
* case) we need to keep the list in order based on the size of the
|
||||
* address mask such that the entry with the widest mask (smallest
|
||||
* numerical value) appears first in the list */
|
||||
list_for_each_entry_rcu(iter, &iface->addr4_list, list)
|
||||
if (iter->valid &&
|
||||
ntohl(entry->mask) > ntohl(iter->mask)) {
|
||||
__list_add_rcu(&entry->list,
|
||||
iter->list.prev,
|
||||
&iter->list);
|
||||
spin_unlock(&netlbl_unlhsh_lock);
|
||||
return 0;
|
||||
}
|
||||
list_add_tail_rcu(&entry->list, &iface->addr4_list);
|
||||
ret_val = netlbl_af4list_add(&entry->list, &iface->addr4_list);
|
||||
spin_unlock(&netlbl_unlhsh_lock);
|
||||
return 0;
|
||||
|
||||
if (ret_val != 0)
|
||||
kfree(entry);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
@ -498,47 +359,29 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
|
||||
const struct in6_addr *mask,
|
||||
u32 secid)
|
||||
{
|
||||
int ret_val;
|
||||
struct netlbl_unlhsh_addr6 *entry;
|
||||
struct netlbl_unlhsh_addr6 *iter;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||
if (entry == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ipv6_addr_copy(&entry->addr, addr);
|
||||
entry->addr.s6_addr32[0] &= mask->s6_addr32[0];
|
||||
entry->addr.s6_addr32[1] &= mask->s6_addr32[1];
|
||||
entry->addr.s6_addr32[2] &= mask->s6_addr32[2];
|
||||
entry->addr.s6_addr32[3] &= mask->s6_addr32[3];
|
||||
ipv6_addr_copy(&entry->mask, mask);
|
||||
entry->secid = secid;
|
||||
entry->valid = 1;
|
||||
ipv6_addr_copy(&entry->list.addr, addr);
|
||||
entry->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
|
||||
entry->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
|
||||
entry->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
|
||||
entry->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
|
||||
ipv6_addr_copy(&entry->list.mask, mask);
|
||||
entry->list.valid = 1;
|
||||
INIT_RCU_HEAD(&entry->rcu);
|
||||
entry->secid = secid;
|
||||
|
||||
spin_lock(&netlbl_unlhsh_lock);
|
||||
iter = netlbl_unlhsh_search_addr6(&entry->addr, iface);
|
||||
if (iter != NULL &&
|
||||
(ipv6_addr_equal(&iter->addr, addr) &&
|
||||
ipv6_addr_equal(&iter->mask, mask))) {
|
||||
spin_unlock(&netlbl_unlhsh_lock);
|
||||
kfree(entry);
|
||||
return -EEXIST;
|
||||
}
|
||||
/* in order to speed up address searches through the list (the common
|
||||
* case) we need to keep the list in order based on the size of the
|
||||
* address mask such that the entry with the widest mask (smallest
|
||||
* numerical value) appears first in the list */
|
||||
list_for_each_entry_rcu(iter, &iface->addr6_list, list)
|
||||
if (iter->valid &&
|
||||
ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) {
|
||||
__list_add_rcu(&entry->list,
|
||||
iter->list.prev,
|
||||
&iter->list);
|
||||
spin_unlock(&netlbl_unlhsh_lock);
|
||||
return 0;
|
||||
}
|
||||
list_add_tail_rcu(&entry->list, &iface->addr6_list);
|
||||
ret_val = netlbl_af6list_add(&entry->list, &iface->addr6_list);
|
||||
spin_unlock(&netlbl_unlhsh_lock);
|
||||
|
||||
if (ret_val != 0)
|
||||
kfree(entry);
|
||||
return 0;
|
||||
}
|
||||
#endif /* IPv6 */
|
||||
@ -658,10 +501,10 @@ static int netlbl_unlhsh_add(struct net *net,
|
||||
mask4 = (struct in_addr *)mask;
|
||||
ret_val = netlbl_unlhsh_add_addr4(iface, addr4, mask4, secid);
|
||||
if (audit_buf != NULL)
|
||||
netlbl_unlabel_audit_addr4(audit_buf,
|
||||
dev_name,
|
||||
addr4->s_addr,
|
||||
mask4->s_addr);
|
||||
netlbl_af4list_audit_addr(audit_buf, 1,
|
||||
dev_name,
|
||||
addr4->s_addr,
|
||||
mask4->s_addr);
|
||||
break;
|
||||
}
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
@ -672,9 +515,9 @@ static int netlbl_unlhsh_add(struct net *net,
|
||||
mask6 = (struct in6_addr *)mask;
|
||||
ret_val = netlbl_unlhsh_add_addr6(iface, addr6, mask6, secid);
|
||||
if (audit_buf != NULL)
|
||||
netlbl_unlabel_audit_addr6(audit_buf,
|
||||
dev_name,
|
||||
addr6, mask6);
|
||||
netlbl_af6list_audit_addr(audit_buf, 1,
|
||||
dev_name,
|
||||
addr6, mask6);
|
||||
break;
|
||||
}
|
||||
#endif /* IPv6 */
|
||||
@ -719,35 +562,34 @@ static int netlbl_unlhsh_remove_addr4(struct net *net,
|
||||
const struct in_addr *mask,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = -ENOENT;
|
||||
int ret_val = 0;
|
||||
struct netlbl_af4list *list_entry;
|
||||
struct netlbl_unlhsh_addr4 *entry;
|
||||
struct audit_buffer *audit_buf = NULL;
|
||||
struct audit_buffer *audit_buf;
|
||||
struct net_device *dev;
|
||||
char *secctx = NULL;
|
||||
char *secctx;
|
||||
u32 secctx_len;
|
||||
|
||||
spin_lock(&netlbl_unlhsh_lock);
|
||||
entry = netlbl_unlhsh_search_addr4(addr->s_addr, iface);
|
||||
if (entry != NULL &&
|
||||
entry->addr == addr->s_addr && entry->mask == mask->s_addr) {
|
||||
entry->valid = 0;
|
||||
list_del_rcu(&entry->list);
|
||||
ret_val = 0;
|
||||
}
|
||||
list_entry = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
|
||||
&iface->addr4_list);
|
||||
spin_unlock(&netlbl_unlhsh_lock);
|
||||
if (list_entry == NULL)
|
||||
ret_val = -ENOENT;
|
||||
entry = netlbl_unlhsh_addr4_entry(list_entry);
|
||||
|
||||
audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
|
||||
audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
dev = dev_get_by_index(net, iface->ifindex);
|
||||
netlbl_unlabel_audit_addr4(audit_buf,
|
||||
(dev != NULL ? dev->name : NULL),
|
||||
entry->addr, entry->mask);
|
||||
netlbl_af4list_audit_addr(audit_buf, 1,
|
||||
(dev != NULL ? dev->name : NULL),
|
||||
addr->s_addr, mask->s_addr);
|
||||
if (dev != NULL)
|
||||
dev_put(dev);
|
||||
if (security_secid_to_secctx(entry->secid,
|
||||
&secctx,
|
||||
&secctx_len) == 0) {
|
||||
if (entry && security_secid_to_secctx(entry->secid,
|
||||
&secctx,
|
||||
&secctx_len) == 0) {
|
||||
audit_log_format(audit_buf, " sec_obj=%s", secctx);
|
||||
security_release_secctx(secctx, secctx_len);
|
||||
}
|
||||
@ -781,36 +623,33 @@ static int netlbl_unlhsh_remove_addr6(struct net *net,
|
||||
const struct in6_addr *mask,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = -ENOENT;
|
||||
int ret_val = 0;
|
||||
struct netlbl_af6list *list_entry;
|
||||
struct netlbl_unlhsh_addr6 *entry;
|
||||
struct audit_buffer *audit_buf = NULL;
|
||||
struct audit_buffer *audit_buf;
|
||||
struct net_device *dev;
|
||||
char *secctx = NULL;
|
||||
char *secctx;
|
||||
u32 secctx_len;
|
||||
|
||||
spin_lock(&netlbl_unlhsh_lock);
|
||||
entry = netlbl_unlhsh_search_addr6(addr, iface);
|
||||
if (entry != NULL &&
|
||||
(ipv6_addr_equal(&entry->addr, addr) &&
|
||||
ipv6_addr_equal(&entry->mask, mask))) {
|
||||
entry->valid = 0;
|
||||
list_del_rcu(&entry->list);
|
||||
ret_val = 0;
|
||||
}
|
||||
list_entry = netlbl_af6list_remove(addr, mask, &iface->addr6_list);
|
||||
spin_unlock(&netlbl_unlhsh_lock);
|
||||
if (list_entry == NULL)
|
||||
ret_val = -ENOENT;
|
||||
entry = netlbl_unlhsh_addr6_entry(list_entry);
|
||||
|
||||
audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
|
||||
audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
dev = dev_get_by_index(net, iface->ifindex);
|
||||
netlbl_unlabel_audit_addr6(audit_buf,
|
||||
(dev != NULL ? dev->name : NULL),
|
||||
addr, mask);
|
||||
netlbl_af6list_audit_addr(audit_buf, 1,
|
||||
(dev != NULL ? dev->name : NULL),
|
||||
addr, mask);
|
||||
if (dev != NULL)
|
||||
dev_put(dev);
|
||||
if (security_secid_to_secctx(entry->secid,
|
||||
&secctx,
|
||||
&secctx_len) == 0) {
|
||||
if (entry && security_secid_to_secctx(entry->secid,
|
||||
&secctx,
|
||||
&secctx_len) == 0) {
|
||||
audit_log_format(audit_buf, " sec_obj=%s", secctx);
|
||||
security_release_secctx(secctx, secctx_len);
|
||||
}
|
||||
@ -836,16 +675,18 @@ static int netlbl_unlhsh_remove_addr6(struct net *net,
|
||||
*/
|
||||
static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface)
|
||||
{
|
||||
struct netlbl_unlhsh_addr4 *iter4;
|
||||
struct netlbl_unlhsh_addr6 *iter6;
|
||||
struct netlbl_af4list *iter4;
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
struct netlbl_af6list *iter6;
|
||||
#endif /* IPv6 */
|
||||
|
||||
spin_lock(&netlbl_unlhsh_lock);
|
||||
list_for_each_entry_rcu(iter4, &iface->addr4_list, list)
|
||||
if (iter4->valid)
|
||||
goto unlhsh_condremove_failure;
|
||||
list_for_each_entry_rcu(iter6, &iface->addr6_list, list)
|
||||
if (iter6->valid)
|
||||
goto unlhsh_condremove_failure;
|
||||
netlbl_af4list_foreach_rcu(iter4, &iface->addr4_list)
|
||||
goto unlhsh_condremove_failure;
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
netlbl_af6list_foreach_rcu(iter6, &iface->addr6_list)
|
||||
goto unlhsh_condremove_failure;
|
||||
#endif /* IPv6 */
|
||||
iface->valid = 0;
|
||||
if (iface->ifindex > 0)
|
||||
list_del_rcu(&iface->list);
|
||||
@ -1349,7 +1190,7 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd,
|
||||
if (addr4) {
|
||||
struct in_addr addr_struct;
|
||||
|
||||
addr_struct.s_addr = addr4->addr;
|
||||
addr_struct.s_addr = addr4->list.addr;
|
||||
ret_val = nla_put(cb_arg->skb,
|
||||
NLBL_UNLABEL_A_IPV4ADDR,
|
||||
sizeof(struct in_addr),
|
||||
@ -1357,7 +1198,7 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd,
|
||||
if (ret_val != 0)
|
||||
goto list_cb_failure;
|
||||
|
||||
addr_struct.s_addr = addr4->mask;
|
||||
addr_struct.s_addr = addr4->list.mask;
|
||||
ret_val = nla_put(cb_arg->skb,
|
||||
NLBL_UNLABEL_A_IPV4MASK,
|
||||
sizeof(struct in_addr),
|
||||
@ -1370,14 +1211,14 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd,
|
||||
ret_val = nla_put(cb_arg->skb,
|
||||
NLBL_UNLABEL_A_IPV6ADDR,
|
||||
sizeof(struct in6_addr),
|
||||
&addr6->addr);
|
||||
&addr6->list.addr);
|
||||
if (ret_val != 0)
|
||||
goto list_cb_failure;
|
||||
|
||||
ret_val = nla_put(cb_arg->skb,
|
||||
NLBL_UNLABEL_A_IPV6MASK,
|
||||
sizeof(struct in6_addr),
|
||||
&addr6->mask);
|
||||
&addr6->list.mask);
|
||||
if (ret_val != 0)
|
||||
goto list_cb_failure;
|
||||
|
||||
@ -1425,8 +1266,11 @@ static int netlbl_unlabel_staticlist(struct sk_buff *skb,
|
||||
u32 iter_bkt;
|
||||
u32 iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0;
|
||||
struct netlbl_unlhsh_iface *iface;
|
||||
struct netlbl_unlhsh_addr4 *addr4;
|
||||
struct netlbl_unlhsh_addr6 *addr6;
|
||||
struct list_head *iter_list;
|
||||
struct netlbl_af4list *addr4;
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
struct netlbl_af6list *addr6;
|
||||
#endif
|
||||
|
||||
cb_arg.nl_cb = cb;
|
||||
cb_arg.skb = skb;
|
||||
@ -1436,44 +1280,43 @@ static int netlbl_unlabel_staticlist(struct sk_buff *skb,
|
||||
for (iter_bkt = skip_bkt;
|
||||
iter_bkt < rcu_dereference(netlbl_unlhsh)->size;
|
||||
iter_bkt++, iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0) {
|
||||
list_for_each_entry_rcu(iface,
|
||||
&rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt],
|
||||
list) {
|
||||
iter_list = &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt];
|
||||
list_for_each_entry_rcu(iface, iter_list, list) {
|
||||
if (!iface->valid ||
|
||||
iter_chain++ < skip_chain)
|
||||
continue;
|
||||
list_for_each_entry_rcu(addr4,
|
||||
&iface->addr4_list,
|
||||
list) {
|
||||
if (!addr4->valid || iter_addr4++ < skip_addr4)
|
||||
netlbl_af4list_foreach_rcu(addr4,
|
||||
&iface->addr4_list) {
|
||||
if (iter_addr4++ < skip_addr4)
|
||||
continue;
|
||||
if (netlbl_unlabel_staticlist_gen(
|
||||
NLBL_UNLABEL_C_STATICLIST,
|
||||
iface,
|
||||
addr4,
|
||||
NULL,
|
||||
&cb_arg) < 0) {
|
||||
NLBL_UNLABEL_C_STATICLIST,
|
||||
iface,
|
||||
netlbl_unlhsh_addr4_entry(addr4),
|
||||
NULL,
|
||||
&cb_arg) < 0) {
|
||||
iter_addr4--;
|
||||
iter_chain--;
|
||||
goto unlabel_staticlist_return;
|
||||
}
|
||||
}
|
||||
list_for_each_entry_rcu(addr6,
|
||||
&iface->addr6_list,
|
||||
list) {
|
||||
if (!addr6->valid || iter_addr6++ < skip_addr6)
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
netlbl_af6list_foreach_rcu(addr6,
|
||||
&iface->addr6_list) {
|
||||
if (iter_addr6++ < skip_addr6)
|
||||
continue;
|
||||
if (netlbl_unlabel_staticlist_gen(
|
||||
NLBL_UNLABEL_C_STATICLIST,
|
||||
iface,
|
||||
NULL,
|
||||
addr6,
|
||||
&cb_arg) < 0) {
|
||||
NLBL_UNLABEL_C_STATICLIST,
|
||||
iface,
|
||||
NULL,
|
||||
netlbl_unlhsh_addr6_entry(addr6),
|
||||
&cb_arg) < 0) {
|
||||
iter_addr6--;
|
||||
iter_chain--;
|
||||
goto unlabel_staticlist_return;
|
||||
}
|
||||
}
|
||||
#endif /* IPv6 */
|
||||
}
|
||||
}
|
||||
|
||||
@ -1504,9 +1347,12 @@ static int netlbl_unlabel_staticlistdef(struct sk_buff *skb,
|
||||
struct netlbl_unlhsh_iface *iface;
|
||||
u32 skip_addr4 = cb->args[0];
|
||||
u32 skip_addr6 = cb->args[1];
|
||||
u32 iter_addr4 = 0, iter_addr6 = 0;
|
||||
struct netlbl_unlhsh_addr4 *addr4;
|
||||
struct netlbl_unlhsh_addr6 *addr6;
|
||||
u32 iter_addr4 = 0;
|
||||
struct netlbl_af4list *addr4;
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
u32 iter_addr6 = 0;
|
||||
struct netlbl_af6list *addr6;
|
||||
#endif
|
||||
|
||||
cb_arg.nl_cb = cb;
|
||||
cb_arg.skb = skb;
|
||||
@ -1517,30 +1363,32 @@ static int netlbl_unlabel_staticlistdef(struct sk_buff *skb,
|
||||
if (iface == NULL || !iface->valid)
|
||||
goto unlabel_staticlistdef_return;
|
||||
|
||||
list_for_each_entry_rcu(addr4, &iface->addr4_list, list) {
|
||||
if (!addr4->valid || iter_addr4++ < skip_addr4)
|
||||
netlbl_af4list_foreach_rcu(addr4, &iface->addr4_list) {
|
||||
if (iter_addr4++ < skip_addr4)
|
||||
continue;
|
||||
if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
|
||||
iface,
|
||||
addr4,
|
||||
NULL,
|
||||
&cb_arg) < 0) {
|
||||
iface,
|
||||
netlbl_unlhsh_addr4_entry(addr4),
|
||||
NULL,
|
||||
&cb_arg) < 0) {
|
||||
iter_addr4--;
|
||||
goto unlabel_staticlistdef_return;
|
||||
}
|
||||
}
|
||||
list_for_each_entry_rcu(addr6, &iface->addr6_list, list) {
|
||||
if (!addr6->valid || iter_addr6++ < skip_addr6)
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
netlbl_af6list_foreach_rcu(addr6, &iface->addr6_list) {
|
||||
if (iter_addr6++ < skip_addr6)
|
||||
continue;
|
||||
if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
|
||||
iface,
|
||||
NULL,
|
||||
addr6,
|
||||
&cb_arg) < 0) {
|
||||
iface,
|
||||
NULL,
|
||||
netlbl_unlhsh_addr6_entry(addr6),
|
||||
&cb_arg) < 0) {
|
||||
iter_addr6--;
|
||||
goto unlabel_staticlistdef_return;
|
||||
}
|
||||
}
|
||||
#endif /* IPv6 */
|
||||
|
||||
unlabel_staticlistdef_return:
|
||||
rcu_read_unlock();
|
||||
@ -1718,25 +1566,27 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb,
|
||||
switch (family) {
|
||||
case PF_INET: {
|
||||
struct iphdr *hdr4;
|
||||
struct netlbl_unlhsh_addr4 *addr4;
|
||||
struct netlbl_af4list *addr4;
|
||||
|
||||
hdr4 = ip_hdr(skb);
|
||||
addr4 = netlbl_unlhsh_search_addr4(hdr4->saddr, iface);
|
||||
addr4 = netlbl_af4list_search(hdr4->saddr,
|
||||
&iface->addr4_list);
|
||||
if (addr4 == NULL)
|
||||
goto unlabel_getattr_nolabel;
|
||||
secattr->attr.secid = addr4->secid;
|
||||
secattr->attr.secid = netlbl_unlhsh_addr4_entry(addr4)->secid;
|
||||
break;
|
||||
}
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
case PF_INET6: {
|
||||
struct ipv6hdr *hdr6;
|
||||
struct netlbl_unlhsh_addr6 *addr6;
|
||||
struct netlbl_af6list *addr6;
|
||||
|
||||
hdr6 = ipv6_hdr(skb);
|
||||
addr6 = netlbl_unlhsh_search_addr6(&hdr6->saddr, iface);
|
||||
addr6 = netlbl_af6list_search(&hdr6->saddr,
|
||||
&iface->addr6_list);
|
||||
if (addr6 == NULL)
|
||||
goto unlabel_getattr_nolabel;
|
||||
secattr->attr.secid = addr6->secid;
|
||||
secattr->attr.secid = netlbl_unlhsh_addr6_entry(addr6)->secid;
|
||||
break;
|
||||
}
|
||||
#endif /* IPv6 */
|
||||
|
@ -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 @@ out:
|
||||
|
||||
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 @@ out:
|
||||
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
|
||||
|
@ -109,16 +109,19 @@ struct netport_security_struct {
|
||||
};
|
||||
|
||||
struct sk_security_struct {
|
||||
u32 sid; /* SID of this object */
|
||||
u32 peer_sid; /* SID of peer */
|
||||
u16 sclass; /* sock security class */
|
||||
#ifdef CONFIG_NETLABEL
|
||||
enum { /* NetLabel state */
|
||||
NLBL_UNSET = 0,
|
||||
NLBL_REQUIRE,
|
||||
NLBL_LABELED,
|
||||
NLBL_REQSKB,
|
||||
NLBL_CONNLABELED,
|
||||
} nlbl_state;
|
||||
struct netlbl_lsm_secattr *nlbl_secattr; /* NetLabel sec attributes */
|
||||
#endif
|
||||
u32 sid; /* SID of this object */
|
||||
u32 peer_sid; /* SID of peer */
|
||||
u16 sclass; /* sock security class */
|
||||
};
|
||||
|
||||
struct key_security_struct {
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007
|
||||
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 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
|
||||
@ -29,8 +29,12 @@
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/netlabel.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
|
||||
#include "objsec.h"
|
||||
#include "security.h"
|
||||
@ -64,32 +68,69 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
|
||||
* @sk: the socket to label
|
||||
* @sid: the SID to use
|
||||
* selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
|
||||
* @sk: the socket
|
||||
*
|
||||
* Description:
|
||||
* Attempt to label a socket using the NetLabel mechanism using the given
|
||||
* SID. Returns zero values on success, negative values on failure.
|
||||
* Generate the NetLabel security attributes for a socket, making full use of
|
||||
* the socket's attribute cache. Returns a pointer to the security attributes
|
||||
* on success, NULL on failure.
|
||||
*
|
||||
*/
|
||||
static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid)
|
||||
static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
|
||||
{
|
||||
int rc;
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
struct netlbl_lsm_secattr secattr;
|
||||
struct netlbl_lsm_secattr *secattr;
|
||||
|
||||
netlbl_secattr_init(&secattr);
|
||||
if (sksec->nlbl_secattr != NULL)
|
||||
return sksec->nlbl_secattr;
|
||||
|
||||
rc = security_netlbl_sid_to_secattr(sid, &secattr);
|
||||
if (rc != 0)
|
||||
goto sock_setsid_return;
|
||||
rc = netlbl_sock_setattr(sk, &secattr);
|
||||
if (rc == 0)
|
||||
secattr = netlbl_secattr_alloc(GFP_ATOMIC);
|
||||
if (secattr == NULL)
|
||||
return NULL;
|
||||
rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
|
||||
if (rc != 0) {
|
||||
netlbl_secattr_free(secattr);
|
||||
return NULL;
|
||||
}
|
||||
sksec->nlbl_secattr = secattr;
|
||||
|
||||
return secattr;
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
|
||||
* @sk: the socket to label
|
||||
*
|
||||
* Description:
|
||||
* Attempt to label a socket using the NetLabel mechanism. Returns zero values
|
||||
* on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
static int selinux_netlbl_sock_setsid(struct sock *sk)
|
||||
{
|
||||
int rc;
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
struct netlbl_lsm_secattr *secattr;
|
||||
|
||||
if (sksec->nlbl_state != NLBL_REQUIRE)
|
||||
return 0;
|
||||
|
||||
secattr = selinux_netlbl_sock_genattr(sk);
|
||||
if (secattr == NULL)
|
||||
return -ENOMEM;
|
||||
rc = netlbl_sock_setattr(sk, secattr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
sksec->nlbl_state = NLBL_LABELED;
|
||||
break;
|
||||
case -EDESTADDRREQ:
|
||||
sksec->nlbl_state = NLBL_REQSKB;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
sock_setsid_return:
|
||||
netlbl_secattr_destroy(&secattr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -105,6 +146,38 @@ void selinux_netlbl_cache_invalidate(void)
|
||||
netlbl_cache_invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_netlbl_err - Handle a NetLabel packet error
|
||||
* @skb: the packet
|
||||
* @error: the error code
|
||||
* @gateway: true if host is acting as a gateway, false otherwise
|
||||
*
|
||||
* Description:
|
||||
* When a packet is dropped due to a call to avc_has_perm() pass the error
|
||||
* code to the NetLabel subsystem so any protocol specific processing can be
|
||||
* done. This is safe to call even if you are unsure if NetLabel labeling is
|
||||
* present on the packet, NetLabel is smart enough to only act when it should.
|
||||
*
|
||||
*/
|
||||
void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
|
||||
{
|
||||
netlbl_skbuff_err(skb, error, gateway);
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_netlbl_sk_security_free - Free the NetLabel fields
|
||||
* @sssec: the sk_security_struct
|
||||
*
|
||||
* Description:
|
||||
* Free all of the memory in the NetLabel fields of a sk_security_struct.
|
||||
*
|
||||
*/
|
||||
void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
|
||||
{
|
||||
if (ssec->nlbl_secattr != NULL)
|
||||
netlbl_secattr_free(ssec->nlbl_secattr);
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_netlbl_sk_security_reset - Reset the NetLabel fields
|
||||
* @ssec: the sk_security_struct
|
||||
@ -163,35 +236,118 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_netlbl_sock_graft - Netlabel the new socket
|
||||
* @sk: the new connection
|
||||
* @sock: the new socket
|
||||
* selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
|
||||
* @skb: the packet
|
||||
* @family: protocol family
|
||||
* @sid: the SID
|
||||
*
|
||||
* Description:
|
||||
* The connection represented by @sk is being grafted onto @sock so set the
|
||||
* socket's NetLabel to match the SID of @sk.
|
||||
* Description
|
||||
* Call the NetLabel mechanism to set the label of a packet using @sid.
|
||||
* Returns zero on auccess, negative values on failure.
|
||||
*
|
||||
*/
|
||||
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
|
||||
int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
|
||||
u16 family,
|
||||
u32 sid)
|
||||
{
|
||||
int rc;
|
||||
struct netlbl_lsm_secattr secattr_storage;
|
||||
struct netlbl_lsm_secattr *secattr = NULL;
|
||||
struct sock *sk;
|
||||
|
||||
/* if this is a locally generated packet check to see if it is already
|
||||
* being labeled by it's parent socket, if it is just exit */
|
||||
sk = skb->sk;
|
||||
if (sk != NULL) {
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
if (sksec->nlbl_state != NLBL_REQSKB)
|
||||
return 0;
|
||||
secattr = sksec->nlbl_secattr;
|
||||
}
|
||||
if (secattr == NULL) {
|
||||
secattr = &secattr_storage;
|
||||
netlbl_secattr_init(secattr);
|
||||
rc = security_netlbl_sid_to_secattr(sid, secattr);
|
||||
if (rc != 0)
|
||||
goto skbuff_setsid_return;
|
||||
}
|
||||
|
||||
rc = netlbl_skbuff_setattr(skb, family, secattr);
|
||||
|
||||
skbuff_setsid_return:
|
||||
if (secattr == &secattr_storage)
|
||||
netlbl_secattr_destroy(secattr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection
|
||||
* @sk: the new connection
|
||||
*
|
||||
* Description:
|
||||
* A new connection has been established on @sk so make sure it is labeled
|
||||
* correctly with the NetLabel susbsystem.
|
||||
*
|
||||
*/
|
||||
void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
|
||||
{
|
||||
int rc;
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
struct netlbl_lsm_secattr secattr;
|
||||
u32 nlbl_peer_sid;
|
||||
struct netlbl_lsm_secattr *secattr;
|
||||
struct inet_sock *sk_inet = inet_sk(sk);
|
||||
struct sockaddr_in addr;
|
||||
|
||||
if (sksec->nlbl_state != NLBL_REQUIRE)
|
||||
return;
|
||||
|
||||
netlbl_secattr_init(&secattr);
|
||||
if (netlbl_sock_getattr(sk, &secattr) == 0 &&
|
||||
secattr.flags != NETLBL_SECATTR_NONE &&
|
||||
security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0)
|
||||
sksec->peer_sid = nlbl_peer_sid;
|
||||
netlbl_secattr_destroy(&secattr);
|
||||
secattr = selinux_netlbl_sock_genattr(sk);
|
||||
if (secattr == NULL)
|
||||
return;
|
||||
|
||||
/* Try to set the NetLabel on the socket to save time later, if we fail
|
||||
* here we will pick up the pieces in later calls to
|
||||
* selinux_netlbl_inode_permission(). */
|
||||
selinux_netlbl_sock_setsid(sk, sksec->sid);
|
||||
rc = netlbl_sock_setattr(sk, secattr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
sksec->nlbl_state = NLBL_LABELED;
|
||||
break;
|
||||
case -EDESTADDRREQ:
|
||||
/* no PF_INET6 support yet because we don't support any IPv6
|
||||
* labeling protocols */
|
||||
if (family != PF_INET) {
|
||||
sksec->nlbl_state = NLBL_UNSET;
|
||||
return;
|
||||
}
|
||||
|
||||
addr.sin_family = family;
|
||||
addr.sin_addr.s_addr = sk_inet->daddr;
|
||||
if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr,
|
||||
secattr) != 0) {
|
||||
/* we failed to label the connected socket (could be
|
||||
* for a variety of reasons, the actual "why" isn't
|
||||
* important here) so we have to go to our backup plan,
|
||||
* labeling the packets individually in the netfilter
|
||||
* local output hook. this is okay but we need to
|
||||
* adjust the MSS of the connection to take into
|
||||
* account any labeling overhead, since we don't know
|
||||
* the exact overhead at this point we'll use the worst
|
||||
* case value which is 40 bytes for IPv4 */
|
||||
struct inet_connection_sock *sk_conn = inet_csk(sk);
|
||||
sk_conn->icsk_ext_hdr_len += 40 -
|
||||
(sk_inet->opt ? sk_inet->opt->optlen : 0);
|
||||
sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
|
||||
|
||||
sksec->nlbl_state = NLBL_REQSKB;
|
||||
} else
|
||||
sksec->nlbl_state = NLBL_CONNLABELED;
|
||||
break;
|
||||
default:
|
||||
/* note that we are failing to label the socket which could be
|
||||
* a bad thing since it means traffic could leave the system
|
||||
* without the desired labeling, however, all is not lost as
|
||||
* we have a check in selinux_netlbl_inode_permission() to
|
||||
* pick up the pieces that we might drop here because we can't
|
||||
* return an error code */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,13 +361,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
|
||||
*/
|
||||
int selinux_netlbl_socket_post_create(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
|
||||
if (sksec->nlbl_state != NLBL_REQUIRE)
|
||||
return 0;
|
||||
|
||||
return selinux_netlbl_sock_setsid(sk, sksec->sid);
|
||||
return selinux_netlbl_sock_setsid(sock->sk);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -246,7 +396,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask)
|
||||
local_bh_disable();
|
||||
bh_lock_sock_nested(sk);
|
||||
if (likely(sksec->nlbl_state == NLBL_REQUIRE))
|
||||
rc = selinux_netlbl_sock_setsid(sk, sksec->sid);
|
||||
rc = selinux_netlbl_sock_setsid(sk);
|
||||
else
|
||||
rc = 0;
|
||||
bh_unlock_sock(sk);
|
||||
@ -307,7 +457,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
|
||||
return 0;
|
||||
|
||||
if (nlbl_sid != SECINITSID_UNLABELED)
|
||||
netlbl_skbuff_err(skb, rc);
|
||||
netlbl_skbuff_err(skb, rc, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -334,7 +484,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
|
||||
struct netlbl_lsm_secattr secattr;
|
||||
|
||||
if (level == IPPROTO_IP && optname == IP_OPTIONS &&
|
||||
sksec->nlbl_state == NLBL_LABELED) {
|
||||
(sksec->nlbl_state == NLBL_LABELED ||
|
||||
sksec->nlbl_state == NLBL_CONNLABELED)) {
|
||||
netlbl_secattr_init(&secattr);
|
||||
lock_sock(sk);
|
||||
rc = netlbl_sock_getattr(sk, &secattr);
|
||||
@ -346,3 +497,50 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_netlbl_socket_connect - Label a client-side socket on connect
|
||||
* @sk: the socket to label
|
||||
* @addr: the destination address
|
||||
*
|
||||
* Description:
|
||||
* Attempt to label a connected socket with NetLabel using the given address.
|
||||
* Returns zero values on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
|
||||
{
|
||||
int rc;
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
struct netlbl_lsm_secattr *secattr;
|
||||
|
||||
if (sksec->nlbl_state != NLBL_REQSKB &&
|
||||
sksec->nlbl_state != NLBL_CONNLABELED)
|
||||
return 0;
|
||||
|
||||
local_bh_disable();
|
||||
bh_lock_sock_nested(sk);
|
||||
|
||||
/* connected sockets are allowed to disconnect when the address family
|
||||
* is set to AF_UNSPEC, if that is what is happening we want to reset
|
||||
* the socket */
|
||||
if (addr->sa_family == AF_UNSPEC) {
|
||||
netlbl_sock_delattr(sk);
|
||||
sksec->nlbl_state = NLBL_REQSKB;
|
||||
rc = 0;
|
||||
goto socket_connect_return;
|
||||
}
|
||||
secattr = selinux_netlbl_sock_genattr(sk);
|
||||
if (secattr == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto socket_connect_return;
|
||||
}
|
||||
rc = netlbl_conn_setattr(sk, addr, secattr);
|
||||
if (rc == 0)
|
||||
sksec->nlbl_state = NLBL_CONNLABELED;
|
||||
|
||||
socket_connect_return:
|
||||
bh_unlock_sock(sk);
|
||||
local_bh_enable();
|
||||
return rc;
|
||||
}
|
||||
|
@ -2955,7 +2955,7 @@ netlbl_secattr_to_sid_return_cleanup:
|
||||
*/
|
||||
int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
|
||||
{
|
||||
int rc = -ENOENT;
|
||||
int rc;
|
||||
struct context *ctx;
|
||||
|
||||
if (!ss_initialized)
|
||||
@ -2963,11 +2963,18 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
ctx = sidtab_search(&sidtab, sid);
|
||||
if (ctx == NULL)
|
||||
if (ctx == NULL) {
|
||||
rc = -ENOENT;
|
||||
goto netlbl_sid_to_secattr_failure;
|
||||
}
|
||||
secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
|
||||
GFP_ATOMIC);
|
||||
secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY;
|
||||
if (secattr->domain == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto netlbl_sid_to_secattr_failure;
|
||||
}
|
||||
secattr->attr.secid = sid;
|
||||
secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;
|
||||
mls_export_netlbl_lvl(ctx, secattr);
|
||||
rc = mls_export_netlbl_cat(ctx, secattr);
|
||||
if (rc != 0)
|
||||
|
@ -2179,7 +2179,10 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
* This is the simplist possible security model
|
||||
* for networking.
|
||||
*/
|
||||
return smk_access(smack, ssp->smk_in, MAY_WRITE);
|
||||
rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
|
||||
if (rc != 0)
|
||||
netlbl_skbuff_err(skb, rc, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -354,9 +354,11 @@ static void smk_cipso_doi(void)
|
||||
doip->tags[rc] = CIPSO_V4_TAG_INVALID;
|
||||
|
||||
rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info);
|
||||
if (rc != 0)
|
||||
if (rc != 0) {
|
||||
printk(KERN_WARNING "%s:%d add rc = %d\n",
|
||||
__func__, __LINE__, rc);
|
||||
kfree(doip);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user