// SPDX-License-Identifier: GPL-2.0-or-later /* * NetLabel Unlabeled Support * * This file defines functions for dealing with unlabeled packets for the * NetLabel system. The NetLabel system manages static and dynamic label * mappings for network protocols such as CIPSO and RIPSO. * * Author: Paul Moore <paul@paul-moore.com>
*/
/* NOTE: at present we always use init's network namespace since we don't * presently support different namespaces even though the majority of
* the functions in this file are "namespace safe" */
/* The unlabeled connection hash table which we use to map network interfaces * and addresses of unlabeled packets to a user specified secid value for the * LSM. The hash table is used to lookup the network interface entry * (struct netlbl_unlhsh_iface) and then the interface entry is used to * lookup an IP address match from an ordered list. If a network interface * match can not be found in the hash table then the default entry * (netlbl_unlhsh_def) is used. The IP address entry list * (struct netlbl_unlhsh_addr) is ordered such that the entries with a * larger netmask come first.
*/ struct netlbl_unlhsh_tbl { struct list_head *tbl;
u32 size;
}; #define netlbl_unlhsh_addr4_entry(iter) \
container_of(iter, struct netlbl_unlhsh_addr4, list) struct netlbl_unlhsh_addr4 {
u32 secid;
/* Unlabeled connection hash table */ /* updates should be so rare that having one spinlock for the entire
* hash table should be okay */ static DEFINE_SPINLOCK(netlbl_unlhsh_lock); #define netlbl_unlhsh_rcu_deref(p) \
rcu_dereference_check(p, lockdep_is_held(&netlbl_unlhsh_lock)) staticstruct netlbl_unlhsh_tbl __rcu *netlbl_unlhsh; staticstruct netlbl_unlhsh_iface __rcu *netlbl_unlhsh_def;
/* Accept unlabeled packets flag */ static u8 netlabel_unlabel_acceptflg;
/* NetLabel Generic NETLINK unlabeled family */ staticstruct genl_family netlbl_unlabel_gnl_family;
/** * netlbl_unlhsh_free_iface - Frees an interface entry from the hash table * @entry: the entry's RCU field * * Description: * This function is designed to be used as a callback to the call_rcu() * function so that memory allocated to a hash table interface entry can be * released safely. It is important to note that this function does not free * the IPv4 and IPv6 address lists contained as part of an interface entry. It * is up to the rest of the code to make sure an interface entry is only freed * once it's address lists are empty. *
*/ staticvoid netlbl_unlhsh_free_iface(struct rcu_head *entry)
{ struct netlbl_unlhsh_iface *iface; struct netlbl_af4list *iter4; struct netlbl_af4list *tmp4; #if IS_ENABLED(CONFIG_IPV6) struct netlbl_af6list *iter6; struct netlbl_af6list *tmp6; #endif/* IPv6 */
/** * netlbl_unlhsh_hash - Hashing function for the hash table * @ifindex: the network interface/device to hash * * Description: * This is the hashing function for the unlabeled hash table, it returns the * bucket number for the given device/interface. The caller is responsible for * ensuring that the hash table is protected with either a RCU read lock or * the hash table lock. *
*/ static u32 netlbl_unlhsh_hash(int ifindex)
{ return ifindex & (netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->size - 1);
}
/** * netlbl_unlhsh_search_iface - Search for a matching interface entry * @ifindex: the network interface * * Description: * Searches the unlabeled connection hash table and returns a pointer to the * interface entry which matches @ifindex, otherwise NULL is returned. The * caller is responsible for ensuring that the hash table is protected with * either a RCU read lock or the hash table lock. *
*/ staticstruct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
{
u32 bkt; struct list_head *bkt_list; struct netlbl_unlhsh_iface *iter;
/** * netlbl_unlhsh_add_addr4 - Add a new IPv4 address entry to the hash table * @iface: the associated interface entry * @addr: IPv4 address in network byte order * @mask: IPv4 address mask in network byte order * @secid: LSM secid value for entry * * Description: * Add a new address entry into the unlabeled connection hash table using the * interface entry specified by @iface. On success zero is returned, otherwise * a negative value is returned. *
*/ staticint netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface, conststruct in_addr *addr, conststruct in_addr *mask,
u32 secid)
{ int ret_val; struct netlbl_unlhsh_addr4 *entry;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) return -ENOMEM;
#if IS_ENABLED(CONFIG_IPV6) /** * netlbl_unlhsh_add_addr6 - Add a new IPv6 address entry to the hash table * @iface: the associated interface entry * @addr: IPv6 address in network byte order * @mask: IPv6 address mask in network byte order * @secid: LSM secid value for entry * * Description: * Add a new address entry into the unlabeled connection hash table using the * interface entry specified by @iface. On success zero is returned, otherwise * a negative value is returned. *
*/ staticint netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface, conststruct in6_addr *addr, conststruct in6_addr *mask,
u32 secid)
{ int ret_val; struct netlbl_unlhsh_addr6 *entry;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) return -ENOMEM;
/** * netlbl_unlhsh_add_iface - Adds a new interface entry to the hash table * @ifindex: network interface * * Description: * Add a new, empty, interface entry into the unlabeled connection hash table. * On success a pointer to the new interface entry is returned, on failure NULL * is returned. *
*/ staticstruct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
{
u32 bkt; struct netlbl_unlhsh_iface *iface;
iface = kzalloc(sizeof(*iface), GFP_ATOMIC); if (iface == NULL) return NULL;
/** * netlbl_unlhsh_add - Adds a new entry to the unlabeled connection hash table * @net: network namespace * @dev_name: interface name * @addr: IP address in network byte order * @mask: address mask in network byte order * @addr_len: length of address/mask (4 for IPv4, 16 for IPv6) * @secid: LSM secid value for the entry * @audit_info: NetLabel audit information * * Description: * Adds a new entry to the unlabeled connection hash table. Returns zero on * success, negative values on failure. *
*/ int netlbl_unlhsh_add(struct net *net, constchar *dev_name, constvoid *addr, constvoid *mask,
u32 addr_len,
u32 secid, struct netlbl_audit *audit_info)
{ int ret_val; int ifindex; struct net_device *dev; struct netlbl_unlhsh_iface *iface; struct audit_buffer *audit_buf = NULL; struct lsm_context ctx;
/** * netlbl_unlhsh_condremove_iface - Remove an interface entry * @iface: the interface entry * * Description: * Remove an interface entry from the unlabeled connection hash table if it is * empty. An interface entry is considered to be empty if there are no * address entries assigned to it. *
*/ staticvoid netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface)
{ struct netlbl_af4list *iter4; #if IS_ENABLED(CONFIG_IPV6) struct netlbl_af6list *iter6; #endif/* IPv6 */
/** * netlbl_unlhsh_remove - Remove an entry from the unlabeled hash table * @net: network namespace * @dev_name: interface name * @addr: IP address in network byte order * @mask: address mask in network byte order * @addr_len: length of address/mask (4 for IPv4, 16 for IPv6) * @audit_info: NetLabel audit information * * Description: * Removes and existing entry from the unlabeled connection hash table. * Returns zero on success, negative values on failure. *
*/ int netlbl_unlhsh_remove(struct net *net, constchar *dev_name, constvoid *addr, constvoid *mask,
u32 addr_len, struct netlbl_audit *audit_info)
{ int ret_val; struct net_device *dev; struct netlbl_unlhsh_iface *iface;
/** * netlbl_unlhsh_netdev_handler - Network device notification handler * @this: notifier block * @event: the event * @ptr: the netdevice notifier info (cast to void) * * Description: * Handle network device events, although at present all we care about is a * network device going away. In the case of a device going away we clear any * related entries from the unlabeled connection hash table. *
*/ staticint netlbl_unlhsh_netdev_handler(struct notifier_block *this, unsignedlong event, void *ptr)
{ struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct netlbl_unlhsh_iface *iface = NULL;
if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE;
/* XXX - should this be a check for NETDEV_DOWN or _UNREGISTER? */ if (event == NETDEV_DOWN) {
spin_lock(&netlbl_unlhsh_lock);
iface = netlbl_unlhsh_search_iface(dev->ifindex); if (iface != NULL && iface->valid) {
iface->valid = 0;
list_del_rcu(&iface->list);
} else
iface = NULL;
spin_unlock(&netlbl_unlhsh_lock);
}
if (iface != NULL)
call_rcu(&iface->rcu, netlbl_unlhsh_free_iface);
return NOTIFY_DONE;
}
/** * netlbl_unlabel_acceptflg_set - Set the unlabeled accept flag * @value: desired value * @audit_info: NetLabel audit information * * Description: * Set the value of the unlabeled accept flag to @value. *
*/ staticvoid netlbl_unlabel_acceptflg_set(u8 value, struct netlbl_audit *audit_info)
{ struct audit_buffer *audit_buf;
u8 old_val;
/** * netlbl_unlabel_addrinfo_get - Get the IPv4/6 address information * @info: the Generic NETLINK info block * @addr: the IP address * @mask: the IP address mask * @len: the address length * * Description: * Examine the Generic NETLINK message and extract the IP address information. * Returns zero on success, negative values on failure. *
*/ staticint netlbl_unlabel_addrinfo_get(struct genl_info *info, void **addr, void **mask,
u32 *len)
{
u32 addr_len;
/** * netlbl_unlabel_accept - Handle an ACCEPT message * @skb: the NETLINK buffer * @info: the Generic NETLINK info block * * Description: * Process a user generated ACCEPT message and set the accept flag accordingly. * Returns zero on success, negative values on failure. *
*/ staticint netlbl_unlabel_accept(struct sk_buff *skb, struct genl_info *info)
{
u8 value; struct netlbl_audit audit_info;
if (info->attrs[NLBL_UNLABEL_A_ACPTFLG]) {
value = nla_get_u8(info->attrs[NLBL_UNLABEL_A_ACPTFLG]); if (value == 1 || value == 0) {
netlbl_netlink_auditinfo(&audit_info);
netlbl_unlabel_acceptflg_set(value, &audit_info); return 0;
}
}
return -EINVAL;
}
/** * netlbl_unlabel_list - Handle a LIST message * @skb: the NETLINK buffer * @info: the Generic NETLINK info block * * Description: * Process a user generated LIST message and respond with the current status. * Returns zero on success, negative values on failure. *
*/ staticint netlbl_unlabel_list(struct sk_buff *skb, struct genl_info *info)
{ int ret_val = -EINVAL; struct sk_buff *ans_skb; void *data;
ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (ans_skb == NULL) goto list_failure;
data = genlmsg_put_reply(ans_skb, info, &netlbl_unlabel_gnl_family,
0, NLBL_UNLABEL_C_LIST); if (data == NULL) {
ret_val = -ENOMEM; goto list_failure;
}
/** * netlbl_unlabel_staticadd - Handle a STATICADD message * @skb: the NETLINK buffer * @info: the Generic NETLINK info block * * Description: * Process a user generated STATICADD message and add a new unlabeled * connection entry to the hash table. Returns zero on success, negative * values on failure. *
*/ staticint netlbl_unlabel_staticadd(struct sk_buff *skb, struct genl_info *info)
{ int ret_val; char *dev_name; void *addr; void *mask;
u32 addr_len;
u32 secid; struct netlbl_audit audit_info;
/* Don't allow users to add both IPv4 and IPv6 addresses for a * single entry. However, allow users to create two entries, one each * for IPv4 and IPv6, with the same LSM security context which should
* achieve the same result. */ if (!info->attrs[NLBL_UNLABEL_A_SECCTX] ||
!info->attrs[NLBL_UNLABEL_A_IFACE] ||
!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
(!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV6MASK]))) return -EINVAL;
/** * netlbl_unlabel_staticadddef - Handle a STATICADDDEF message * @skb: the NETLINK buffer * @info: the Generic NETLINK info block * * Description: * Process a user generated STATICADDDEF message and add a new default * unlabeled connection entry. Returns zero on success, negative values on * failure. *
*/ staticint netlbl_unlabel_staticadddef(struct sk_buff *skb, struct genl_info *info)
{ int ret_val; void *addr; void *mask;
u32 addr_len;
u32 secid; struct netlbl_audit audit_info;
/* Don't allow users to add both IPv4 and IPv6 addresses for a * single entry. However, allow users to create two entries, one each * for IPv4 and IPv6, with the same LSM security context which should
* achieve the same result. */ if (!info->attrs[NLBL_UNLABEL_A_SECCTX] ||
!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
(!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV6MASK]))) return -EINVAL;
/** * netlbl_unlabel_staticremove - Handle a STATICREMOVE message * @skb: the NETLINK buffer * @info: the Generic NETLINK info block * * Description: * Process a user generated STATICREMOVE message and remove the specified * unlabeled connection entry. Returns zero on success, negative values on * failure. *
*/ staticint netlbl_unlabel_staticremove(struct sk_buff *skb, struct genl_info *info)
{ int ret_val; char *dev_name; void *addr; void *mask;
u32 addr_len; struct netlbl_audit audit_info;
/* See the note in netlbl_unlabel_staticadd() about not allowing both
* IPv4 and IPv6 in the same entry. */ if (!info->attrs[NLBL_UNLABEL_A_IFACE] ||
!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
(!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV6MASK]))) return -EINVAL;
/** * netlbl_unlabel_staticremovedef - Handle a STATICREMOVEDEF message * @skb: the NETLINK buffer * @info: the Generic NETLINK info block * * Description: * Process a user generated STATICREMOVEDEF message and remove the default * unlabeled connection entry. Returns zero on success, negative values on * failure. *
*/ staticint netlbl_unlabel_staticremovedef(struct sk_buff *skb, struct genl_info *info)
{ int ret_val; void *addr; void *mask;
u32 addr_len; struct netlbl_audit audit_info;
/* See the note in netlbl_unlabel_staticadd() about not allowing both
* IPv4 and IPv6 in the same entry. */ if (!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
(!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV6MASK]))) return -EINVAL;
/** * netlbl_unlabel_staticlist_gen - Generate messages for STATICLIST[DEF] * @cmd: command/message * @iface: the interface entry * @addr4: the IPv4 address entry * @addr6: the IPv6 address entry * @arg: the netlbl_unlhsh_walk_arg structure * * Description: * This function is designed to be used to generate a response for a * STATICLIST or STATICLISTDEF message. When called either @addr4 or @addr6 * can be specified, not both, the other unspecified entry should be set to * NULL by the caller. Returns the size of the message on success, negative * values on failure. *
*/ staticint netlbl_unlabel_staticlist_gen(u32 cmd, conststruct netlbl_unlhsh_iface *iface, conststruct netlbl_unlhsh_addr4 *addr4, conststruct netlbl_unlhsh_addr6 *addr6, void *arg)
{ int ret_val = -ENOMEM; struct netlbl_unlhsh_walk_arg *cb_arg = arg; struct net_device *dev; struct lsm_context ctx; void *data;
u32 secid;
data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
cb_arg->seq, &netlbl_unlabel_gnl_family,
NLM_F_MULTI, cmd); if (data == NULL) goto list_cb_failure;
if (iface->ifindex > 0) {
dev = dev_get_by_index(&init_net, iface->ifindex); if (!dev) {
ret_val = -ENODEV; goto list_cb_failure;
}
ret_val = nla_put_string(cb_arg->skb,
NLBL_UNLABEL_A_IFACE, dev->name);
dev_put(dev); if (ret_val != 0) goto list_cb_failure;
}
/** * netlbl_unlabel_init - Initialize the unlabeled connection hash table * @size: the number of bits to use for the hash buckets * * Description: * Initializes the unlabeled connection hash table and registers a network * device notification handler. This function should only be called by the * NetLabel subsystem itself during initialization. Returns zero on success, * non-zero values on error. *
*/ int __init netlbl_unlabel_init(u32 size)
{
u32 iter; struct netlbl_unlhsh_tbl *hsh_tbl;
if (size == 0) return -EINVAL;
hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL); if (hsh_tbl == NULL) return -ENOMEM;
hsh_tbl->size = 1 << size;
hsh_tbl->tbl = kcalloc(hsh_tbl->size, sizeof(struct list_head),
GFP_KERNEL); if (hsh_tbl->tbl == NULL) {
kfree(hsh_tbl); return -ENOMEM;
} for (iter = 0; iter < hsh_tbl->size; iter++)
INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
/** * netlbl_unlabel_getattr - Get the security attributes for an unlabled packet * @skb: the packet * @family: protocol family * @secattr: the security attributes * * Description: * Determine the security attributes, if any, for an unlabled packet and return * them in @secattr. Returns zero on success and negative values on failure. *
*/ int netlbl_unlabel_getattr(conststruct sk_buff *skb,
u16 family, struct netlbl_lsm_secattr *secattr)
{ struct netlbl_unlhsh_iface *iface;
rcu_read_lock();
iface = netlbl_unlhsh_search_iface(skb->skb_iif); if (iface == NULL)
iface = rcu_dereference(netlbl_unlhsh_def); if (iface == NULL || !iface->valid) goto unlabel_getattr_nolabel;
#if IS_ENABLED(CONFIG_IPV6) /* When resolving a fallback label, check the sk_buff version as * it is possible (e.g. SCTP) to have family = PF_INET6 while * receiving ip_hdr(skb)->version = 4.
*/ if (family == PF_INET6 && ip_hdr(skb)->version == 4)
family = PF_INET; #endif/* IPv6 */
/** * netlbl_unlabel_defconf - Set the default config to allow unlabeled packets * * Description: * Set the default NetLabel configuration to allow incoming unlabeled packets * and to send unlabeled network traffic by default. *
*/ int __init netlbl_unlabel_defconf(void)
{ int ret_val; struct netlbl_dom_map *entry; struct netlbl_audit audit_info;
/* Only the kernel is allowed to call this function and the only time * it is called is at bootup before the audit subsystem is reporting
* messages so don't worry to much about these values. */
security_current_getlsmprop_subj(&audit_info.prop);
audit_info.loginuid = GLOBAL_ROOT_UID;
audit_info.sessionid = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.