cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

netlabel_mgmt.c (22174B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * NetLabel Management Support
      4 *
      5 * This file defines the management functions for the NetLabel system.  The
      6 * NetLabel system manages static and dynamic label mappings for network
      7 * protocols such as CIPSO and RIPSO.
      8 *
      9 * Author: Paul Moore <paul@paul-moore.com>
     10 */
     11
     12/*
     13 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
     14 */
     15
     16#include <linux/types.h>
     17#include <linux/socket.h>
     18#include <linux/string.h>
     19#include <linux/skbuff.h>
     20#include <linux/in.h>
     21#include <linux/in6.h>
     22#include <linux/slab.h>
     23#include <net/sock.h>
     24#include <net/netlink.h>
     25#include <net/genetlink.h>
     26#include <net/ip.h>
     27#include <net/ipv6.h>
     28#include <net/netlabel.h>
     29#include <net/cipso_ipv4.h>
     30#include <net/calipso.h>
     31#include <linux/atomic.h>
     32
     33#include "netlabel_calipso.h"
     34#include "netlabel_domainhash.h"
     35#include "netlabel_user.h"
     36#include "netlabel_mgmt.h"
     37
     38/* NetLabel configured protocol counter */
     39atomic_t netlabel_mgmt_protocount = ATOMIC_INIT(0);
     40
     41/* Argument struct for netlbl_domhsh_walk() */
     42struct netlbl_domhsh_walk_arg {
     43	struct netlink_callback *nl_cb;
     44	struct sk_buff *skb;
     45	u32 seq;
     46};
     47
     48/* NetLabel Generic NETLINK CIPSOv4 family */
     49static struct genl_family netlbl_mgmt_gnl_family;
     50
     51/* NetLabel Netlink attribute policy */
     52static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
     53	[NLBL_MGMT_A_DOMAIN] = { .type = NLA_NUL_STRING },
     54	[NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 },
     55	[NLBL_MGMT_A_VERSION] = { .type = NLA_U32 },
     56	[NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 },
     57	[NLBL_MGMT_A_FAMILY] = { .type = NLA_U16 },
     58	[NLBL_MGMT_A_CLPDOI] = { .type = NLA_U32 },
     59};
     60
     61/*
     62 * Helper Functions
     63 */
     64
     65/**
     66 * netlbl_mgmt_add_common - Handle an ADD message
     67 * @info: the Generic NETLINK info block
     68 * @audit_info: NetLabel audit information
     69 *
     70 * Description:
     71 * Helper function for the ADD and ADDDEF messages to add the domain mappings
     72 * from the message to the hash table.  See netlabel.h for a description of the
     73 * message format.  Returns zero on success, negative values on failure.
     74 *
     75 */
     76static int netlbl_mgmt_add_common(struct genl_info *info,
     77				  struct netlbl_audit *audit_info)
     78{
     79	void *pmap = NULL;
     80	int ret_val = -EINVAL;
     81	struct netlbl_domaddr_map *addrmap = NULL;
     82	struct cipso_v4_doi *cipsov4 = NULL;
     83#if IS_ENABLED(CONFIG_IPV6)
     84	struct calipso_doi *calipso = NULL;
     85#endif
     86	u32 tmp_val;
     87	struct netlbl_dom_map *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
     88
     89	if (!entry)
     90		return -ENOMEM;
     91	entry->def.type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
     92	if (info->attrs[NLBL_MGMT_A_DOMAIN]) {
     93		size_t tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
     94		entry->domain = kmalloc(tmp_size, GFP_KERNEL);
     95		if (entry->domain == NULL) {
     96			ret_val = -ENOMEM;
     97			goto add_free_entry;
     98		}
     99		nla_strscpy(entry->domain,
    100			    info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
    101	}
    102
    103	/* NOTE: internally we allow/use a entry->def.type value of
    104	 *       NETLBL_NLTYPE_ADDRSELECT but we don't currently allow users
    105	 *       to pass that as a protocol value because we need to know the
    106	 *       "real" protocol */
    107
    108	switch (entry->def.type) {
    109	case NETLBL_NLTYPE_UNLABELED:
    110		if (info->attrs[NLBL_MGMT_A_FAMILY])
    111			entry->family =
    112				nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]);
    113		else
    114			entry->family = AF_UNSPEC;
    115		break;
    116	case NETLBL_NLTYPE_CIPSOV4:
    117		if (!info->attrs[NLBL_MGMT_A_CV4DOI])
    118			goto add_free_domain;
    119
    120		tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
    121		cipsov4 = cipso_v4_doi_getdef(tmp_val);
    122		if (cipsov4 == NULL)
    123			goto add_free_domain;
    124		entry->family = AF_INET;
    125		entry->def.cipso = cipsov4;
    126		break;
    127#if IS_ENABLED(CONFIG_IPV6)
    128	case NETLBL_NLTYPE_CALIPSO:
    129		if (!info->attrs[NLBL_MGMT_A_CLPDOI])
    130			goto add_free_domain;
    131
    132		tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CLPDOI]);
    133		calipso = calipso_doi_getdef(tmp_val);
    134		if (calipso == NULL)
    135			goto add_free_domain;
    136		entry->family = AF_INET6;
    137		entry->def.calipso = calipso;
    138		break;
    139#endif /* IPv6 */
    140	default:
    141		goto add_free_domain;
    142	}
    143
    144	if ((entry->family == AF_INET && info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
    145	    (entry->family == AF_INET6 && info->attrs[NLBL_MGMT_A_IPV4ADDR]))
    146		goto add_doi_put_def;
    147
    148	if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) {
    149		struct in_addr *addr;
    150		struct in_addr *mask;
    151		struct netlbl_domaddr4_map *map;
    152
    153		addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
    154		if (addrmap == NULL) {
    155			ret_val = -ENOMEM;
    156			goto add_doi_put_def;
    157		}
    158		INIT_LIST_HEAD(&addrmap->list4);
    159		INIT_LIST_HEAD(&addrmap->list6);
    160
    161		if (nla_len(info->attrs[NLBL_MGMT_A_IPV4ADDR]) !=
    162		    sizeof(struct in_addr)) {
    163			ret_val = -EINVAL;
    164			goto add_free_addrmap;
    165		}
    166		if (nla_len(info->attrs[NLBL_MGMT_A_IPV4MASK]) !=
    167		    sizeof(struct in_addr)) {
    168			ret_val = -EINVAL;
    169			goto add_free_addrmap;
    170		}
    171		addr = nla_data(info->attrs[NLBL_MGMT_A_IPV4ADDR]);
    172		mask = nla_data(info->attrs[NLBL_MGMT_A_IPV4MASK]);
    173
    174		map = kzalloc(sizeof(*map), GFP_KERNEL);
    175		if (map == NULL) {
    176			ret_val = -ENOMEM;
    177			goto add_free_addrmap;
    178		}
    179		pmap = map;
    180		map->list.addr = addr->s_addr & mask->s_addr;
    181		map->list.mask = mask->s_addr;
    182		map->list.valid = 1;
    183		map->def.type = entry->def.type;
    184		if (cipsov4)
    185			map->def.cipso = cipsov4;
    186
    187		ret_val = netlbl_af4list_add(&map->list, &addrmap->list4);
    188		if (ret_val != 0)
    189			goto add_free_map;
    190
    191		entry->family = AF_INET;
    192		entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
    193		entry->def.addrsel = addrmap;
    194#if IS_ENABLED(CONFIG_IPV6)
    195	} else if (info->attrs[NLBL_MGMT_A_IPV6ADDR]) {
    196		struct in6_addr *addr;
    197		struct in6_addr *mask;
    198		struct netlbl_domaddr6_map *map;
    199
    200		addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
    201		if (addrmap == NULL) {
    202			ret_val = -ENOMEM;
    203			goto add_doi_put_def;
    204		}
    205		INIT_LIST_HEAD(&addrmap->list4);
    206		INIT_LIST_HEAD(&addrmap->list6);
    207
    208		if (nla_len(info->attrs[NLBL_MGMT_A_IPV6ADDR]) !=
    209		    sizeof(struct in6_addr)) {
    210			ret_val = -EINVAL;
    211			goto add_free_addrmap;
    212		}
    213		if (nla_len(info->attrs[NLBL_MGMT_A_IPV6MASK]) !=
    214		    sizeof(struct in6_addr)) {
    215			ret_val = -EINVAL;
    216			goto add_free_addrmap;
    217		}
    218		addr = nla_data(info->attrs[NLBL_MGMT_A_IPV6ADDR]);
    219		mask = nla_data(info->attrs[NLBL_MGMT_A_IPV6MASK]);
    220
    221		map = kzalloc(sizeof(*map), GFP_KERNEL);
    222		if (map == NULL) {
    223			ret_val = -ENOMEM;
    224			goto add_free_addrmap;
    225		}
    226		pmap = map;
    227		map->list.addr = *addr;
    228		map->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
    229		map->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
    230		map->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
    231		map->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
    232		map->list.mask = *mask;
    233		map->list.valid = 1;
    234		map->def.type = entry->def.type;
    235		if (calipso)
    236			map->def.calipso = calipso;
    237
    238		ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
    239		if (ret_val != 0)
    240			goto add_free_map;
    241
    242		entry->family = AF_INET6;
    243		entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
    244		entry->def.addrsel = addrmap;
    245#endif /* IPv6 */
    246	}
    247
    248	ret_val = netlbl_domhsh_add(entry, audit_info);
    249	if (ret_val != 0)
    250		goto add_free_map;
    251
    252	return 0;
    253
    254add_free_map:
    255	kfree(pmap);
    256add_free_addrmap:
    257	kfree(addrmap);
    258add_doi_put_def:
    259	cipso_v4_doi_putdef(cipsov4);
    260#if IS_ENABLED(CONFIG_IPV6)
    261	calipso_doi_putdef(calipso);
    262#endif
    263add_free_domain:
    264	kfree(entry->domain);
    265add_free_entry:
    266	kfree(entry);
    267	return ret_val;
    268}
    269
    270/**
    271 * netlbl_mgmt_listentry - List a NetLabel/LSM domain map entry
    272 * @skb: the NETLINK buffer
    273 * @entry: the map entry
    274 *
    275 * Description:
    276 * This function is a helper function used by the LISTALL and LISTDEF command
    277 * handlers.  The caller is responsible for ensuring that the RCU read lock
    278 * is held.  Returns zero on success, negative values on failure.
    279 *
    280 */
    281static int netlbl_mgmt_listentry(struct sk_buff *skb,
    282				 struct netlbl_dom_map *entry)
    283{
    284	int ret_val = 0;
    285	struct nlattr *nla_a;
    286	struct nlattr *nla_b;
    287	struct netlbl_af4list *iter4;
    288#if IS_ENABLED(CONFIG_IPV6)
    289	struct netlbl_af6list *iter6;
    290#endif
    291
    292	if (entry->domain != NULL) {
    293		ret_val = nla_put_string(skb,
    294					 NLBL_MGMT_A_DOMAIN, entry->domain);
    295		if (ret_val != 0)
    296			return ret_val;
    297	}
    298
    299	ret_val = nla_put_u16(skb, NLBL_MGMT_A_FAMILY, entry->family);
    300	if (ret_val != 0)
    301		return ret_val;
    302
    303	switch (entry->def.type) {
    304	case NETLBL_NLTYPE_ADDRSELECT:
    305		nla_a = nla_nest_start_noflag(skb, NLBL_MGMT_A_SELECTORLIST);
    306		if (nla_a == NULL)
    307			return -ENOMEM;
    308
    309		netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4) {
    310			struct netlbl_domaddr4_map *map4;
    311			struct in_addr addr_struct;
    312
    313			nla_b = nla_nest_start_noflag(skb,
    314						      NLBL_MGMT_A_ADDRSELECTOR);
    315			if (nla_b == NULL)
    316				return -ENOMEM;
    317
    318			addr_struct.s_addr = iter4->addr;
    319			ret_val = nla_put_in_addr(skb, NLBL_MGMT_A_IPV4ADDR,
    320						  addr_struct.s_addr);
    321			if (ret_val != 0)
    322				return ret_val;
    323			addr_struct.s_addr = iter4->mask;
    324			ret_val = nla_put_in_addr(skb, NLBL_MGMT_A_IPV4MASK,
    325						  addr_struct.s_addr);
    326			if (ret_val != 0)
    327				return ret_val;
    328			map4 = netlbl_domhsh_addr4_entry(iter4);
    329			ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
    330					      map4->def.type);
    331			if (ret_val != 0)
    332				return ret_val;
    333			switch (map4->def.type) {
    334			case NETLBL_NLTYPE_CIPSOV4:
    335				ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
    336						      map4->def.cipso->doi);
    337				if (ret_val != 0)
    338					return ret_val;
    339				break;
    340			}
    341
    342			nla_nest_end(skb, nla_b);
    343		}
    344#if IS_ENABLED(CONFIG_IPV6)
    345		netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6) {
    346			struct netlbl_domaddr6_map *map6;
    347
    348			nla_b = nla_nest_start_noflag(skb,
    349						      NLBL_MGMT_A_ADDRSELECTOR);
    350			if (nla_b == NULL)
    351				return -ENOMEM;
    352
    353			ret_val = nla_put_in6_addr(skb, NLBL_MGMT_A_IPV6ADDR,
    354						   &iter6->addr);
    355			if (ret_val != 0)
    356				return ret_val;
    357			ret_val = nla_put_in6_addr(skb, NLBL_MGMT_A_IPV6MASK,
    358						   &iter6->mask);
    359			if (ret_val != 0)
    360				return ret_val;
    361			map6 = netlbl_domhsh_addr6_entry(iter6);
    362			ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
    363					      map6->def.type);
    364			if (ret_val != 0)
    365				return ret_val;
    366
    367			switch (map6->def.type) {
    368			case NETLBL_NLTYPE_CALIPSO:
    369				ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI,
    370						      map6->def.calipso->doi);
    371				if (ret_val != 0)
    372					return ret_val;
    373				break;
    374			}
    375
    376			nla_nest_end(skb, nla_b);
    377		}
    378#endif /* IPv6 */
    379
    380		nla_nest_end(skb, nla_a);
    381		break;
    382	case NETLBL_NLTYPE_UNLABELED:
    383		ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
    384				      entry->def.type);
    385		break;
    386	case NETLBL_NLTYPE_CIPSOV4:
    387		ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
    388				      entry->def.type);
    389		if (ret_val != 0)
    390			return ret_val;
    391		ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
    392				      entry->def.cipso->doi);
    393		break;
    394	case NETLBL_NLTYPE_CALIPSO:
    395		ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
    396				      entry->def.type);
    397		if (ret_val != 0)
    398			return ret_val;
    399		ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI,
    400				      entry->def.calipso->doi);
    401		break;
    402	}
    403
    404	return ret_val;
    405}
    406
    407/*
    408 * NetLabel Command Handlers
    409 */
    410
    411/**
    412 * netlbl_mgmt_add - Handle an ADD message
    413 * @skb: the NETLINK buffer
    414 * @info: the Generic NETLINK info block
    415 *
    416 * Description:
    417 * Process a user generated ADD message and add the domains from the message
    418 * to the hash table.  See netlabel.h for a description of the message format.
    419 * Returns zero on success, negative values on failure.
    420 *
    421 */
    422static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
    423{
    424	struct netlbl_audit audit_info;
    425
    426	if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) ||
    427	    (!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
    428	    (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
    429	     info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
    430	    (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
    431	     info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
    432	    ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
    433	     (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
    434	    ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
    435	     (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
    436		return -EINVAL;
    437
    438	netlbl_netlink_auditinfo(&audit_info);
    439
    440	return netlbl_mgmt_add_common(info, &audit_info);
    441}
    442
    443/**
    444 * netlbl_mgmt_remove - Handle a REMOVE message
    445 * @skb: the NETLINK buffer
    446 * @info: the Generic NETLINK info block
    447 *
    448 * Description:
    449 * Process a user generated REMOVE message and remove the specified domain
    450 * mappings.  Returns zero on success, negative values on failure.
    451 *
    452 */
    453static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info)
    454{
    455	char *domain;
    456	struct netlbl_audit audit_info;
    457
    458	if (!info->attrs[NLBL_MGMT_A_DOMAIN])
    459		return -EINVAL;
    460
    461	netlbl_netlink_auditinfo(&audit_info);
    462
    463	domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]);
    464	return netlbl_domhsh_remove(domain, AF_UNSPEC, &audit_info);
    465}
    466
    467/**
    468 * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL
    469 * @entry: the domain mapping hash table entry
    470 * @arg: the netlbl_domhsh_walk_arg structure
    471 *
    472 * Description:
    473 * This function is designed to be used as a callback to the
    474 * netlbl_domhsh_walk() function for use in generating a response for a LISTALL
    475 * message.  Returns the size of the message on success, negative values on
    476 * failure.
    477 *
    478 */
    479static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg)
    480{
    481	int ret_val = -ENOMEM;
    482	struct netlbl_domhsh_walk_arg *cb_arg = arg;
    483	void *data;
    484
    485	data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
    486			   cb_arg->seq, &netlbl_mgmt_gnl_family,
    487			   NLM_F_MULTI, NLBL_MGMT_C_LISTALL);
    488	if (data == NULL)
    489		goto listall_cb_failure;
    490
    491	ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry);
    492	if (ret_val != 0)
    493		goto listall_cb_failure;
    494
    495	cb_arg->seq++;
    496	genlmsg_end(cb_arg->skb, data);
    497	return 0;
    498
    499listall_cb_failure:
    500	genlmsg_cancel(cb_arg->skb, data);
    501	return ret_val;
    502}
    503
    504/**
    505 * netlbl_mgmt_listall - Handle a LISTALL message
    506 * @skb: the NETLINK buffer
    507 * @cb: the NETLINK callback
    508 *
    509 * Description:
    510 * Process a user generated LISTALL message and dumps the domain hash table in
    511 * a form suitable for use in a kernel generated LISTALL message.  Returns zero
    512 * on success, negative values on failure.
    513 *
    514 */
    515static int netlbl_mgmt_listall(struct sk_buff *skb,
    516			       struct netlink_callback *cb)
    517{
    518	struct netlbl_domhsh_walk_arg cb_arg;
    519	u32 skip_bkt = cb->args[0];
    520	u32 skip_chain = cb->args[1];
    521
    522	cb_arg.nl_cb = cb;
    523	cb_arg.skb = skb;
    524	cb_arg.seq = cb->nlh->nlmsg_seq;
    525
    526	netlbl_domhsh_walk(&skip_bkt,
    527			   &skip_chain,
    528			   netlbl_mgmt_listall_cb,
    529			   &cb_arg);
    530
    531	cb->args[0] = skip_bkt;
    532	cb->args[1] = skip_chain;
    533	return skb->len;
    534}
    535
    536/**
    537 * netlbl_mgmt_adddef - Handle an ADDDEF message
    538 * @skb: the NETLINK buffer
    539 * @info: the Generic NETLINK info block
    540 *
    541 * Description:
    542 * Process a user generated ADDDEF message and respond accordingly.  Returns
    543 * zero on success, negative values on failure.
    544 *
    545 */
    546static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
    547{
    548	struct netlbl_audit audit_info;
    549
    550	if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
    551	    (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
    552	     info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
    553	    (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
    554	     info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
    555	    ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
    556	     (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
    557	    ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
    558	     (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
    559		return -EINVAL;
    560
    561	netlbl_netlink_auditinfo(&audit_info);
    562
    563	return netlbl_mgmt_add_common(info, &audit_info);
    564}
    565
    566/**
    567 * netlbl_mgmt_removedef - Handle a REMOVEDEF message
    568 * @skb: the NETLINK buffer
    569 * @info: the Generic NETLINK info block
    570 *
    571 * Description:
    572 * Process a user generated REMOVEDEF message and remove the default domain
    573 * mapping.  Returns zero on success, negative values on failure.
    574 *
    575 */
    576static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info)
    577{
    578	struct netlbl_audit audit_info;
    579
    580	netlbl_netlink_auditinfo(&audit_info);
    581
    582	return netlbl_domhsh_remove_default(AF_UNSPEC, &audit_info);
    583}
    584
    585/**
    586 * netlbl_mgmt_listdef - Handle a LISTDEF message
    587 * @skb: the NETLINK buffer
    588 * @info: the Generic NETLINK info block
    589 *
    590 * Description:
    591 * Process a user generated LISTDEF message and dumps the default domain
    592 * mapping in a form suitable for use in a kernel generated LISTDEF message.
    593 * Returns zero on success, negative values on failure.
    594 *
    595 */
    596static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
    597{
    598	int ret_val = -ENOMEM;
    599	struct sk_buff *ans_skb = NULL;
    600	void *data;
    601	struct netlbl_dom_map *entry;
    602	u16 family;
    603
    604	if (info->attrs[NLBL_MGMT_A_FAMILY])
    605		family = nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]);
    606	else
    607		family = AF_INET;
    608
    609	ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
    610	if (ans_skb == NULL)
    611		return -ENOMEM;
    612	data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family,
    613				 0, NLBL_MGMT_C_LISTDEF);
    614	if (data == NULL)
    615		goto listdef_failure;
    616
    617	rcu_read_lock();
    618	entry = netlbl_domhsh_getentry(NULL, family);
    619	if (entry == NULL) {
    620		ret_val = -ENOENT;
    621		goto listdef_failure_lock;
    622	}
    623	ret_val = netlbl_mgmt_listentry(ans_skb, entry);
    624	rcu_read_unlock();
    625	if (ret_val != 0)
    626		goto listdef_failure;
    627
    628	genlmsg_end(ans_skb, data);
    629	return genlmsg_reply(ans_skb, info);
    630
    631listdef_failure_lock:
    632	rcu_read_unlock();
    633listdef_failure:
    634	kfree_skb(ans_skb);
    635	return ret_val;
    636}
    637
    638/**
    639 * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response
    640 * @skb: the skb to write to
    641 * @cb: the NETLINK callback
    642 * @protocol: the NetLabel protocol to use in the message
    643 *
    644 * Description:
    645 * This function is to be used in conjunction with netlbl_mgmt_protocols() to
    646 * answer a application's PROTOCOLS message.  Returns the size of the message
    647 * on success, negative values on failure.
    648 *
    649 */
    650static int netlbl_mgmt_protocols_cb(struct sk_buff *skb,
    651				    struct netlink_callback *cb,
    652				    u32 protocol)
    653{
    654	int ret_val = -ENOMEM;
    655	void *data;
    656
    657	data = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
    658			   &netlbl_mgmt_gnl_family, NLM_F_MULTI,
    659			   NLBL_MGMT_C_PROTOCOLS);
    660	if (data == NULL)
    661		goto protocols_cb_failure;
    662
    663	ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol);
    664	if (ret_val != 0)
    665		goto protocols_cb_failure;
    666
    667	genlmsg_end(skb, data);
    668	return 0;
    669
    670protocols_cb_failure:
    671	genlmsg_cancel(skb, data);
    672	return ret_val;
    673}
    674
    675/**
    676 * netlbl_mgmt_protocols - Handle a PROTOCOLS message
    677 * @skb: the NETLINK buffer
    678 * @cb: the NETLINK callback
    679 *
    680 * Description:
    681 * Process a user generated PROTOCOLS message and respond accordingly.
    682 *
    683 */
    684static int netlbl_mgmt_protocols(struct sk_buff *skb,
    685				 struct netlink_callback *cb)
    686{
    687	u32 protos_sent = cb->args[0];
    688
    689	if (protos_sent == 0) {
    690		if (netlbl_mgmt_protocols_cb(skb,
    691					     cb,
    692					     NETLBL_NLTYPE_UNLABELED) < 0)
    693			goto protocols_return;
    694		protos_sent++;
    695	}
    696	if (protos_sent == 1) {
    697		if (netlbl_mgmt_protocols_cb(skb,
    698					     cb,
    699					     NETLBL_NLTYPE_CIPSOV4) < 0)
    700			goto protocols_return;
    701		protos_sent++;
    702	}
    703#if IS_ENABLED(CONFIG_IPV6)
    704	if (protos_sent == 2) {
    705		if (netlbl_mgmt_protocols_cb(skb,
    706					     cb,
    707					     NETLBL_NLTYPE_CALIPSO) < 0)
    708			goto protocols_return;
    709		protos_sent++;
    710	}
    711#endif
    712
    713protocols_return:
    714	cb->args[0] = protos_sent;
    715	return skb->len;
    716}
    717
    718/**
    719 * netlbl_mgmt_version - Handle a VERSION message
    720 * @skb: the NETLINK buffer
    721 * @info: the Generic NETLINK info block
    722 *
    723 * Description:
    724 * Process a user generated VERSION message and respond accordingly.  Returns
    725 * zero on success, negative values on failure.
    726 *
    727 */
    728static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info)
    729{
    730	int ret_val = -ENOMEM;
    731	struct sk_buff *ans_skb = NULL;
    732	void *data;
    733
    734	ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
    735	if (ans_skb == NULL)
    736		return -ENOMEM;
    737	data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family,
    738				 0, NLBL_MGMT_C_VERSION);
    739	if (data == NULL)
    740		goto version_failure;
    741
    742	ret_val = nla_put_u32(ans_skb,
    743			      NLBL_MGMT_A_VERSION,
    744			      NETLBL_PROTO_VERSION);
    745	if (ret_val != 0)
    746		goto version_failure;
    747
    748	genlmsg_end(ans_skb, data);
    749	return genlmsg_reply(ans_skb, info);
    750
    751version_failure:
    752	kfree_skb(ans_skb);
    753	return ret_val;
    754}
    755
    756
    757/*
    758 * NetLabel Generic NETLINK Command Definitions
    759 */
    760
    761static const struct genl_small_ops netlbl_mgmt_genl_ops[] = {
    762	{
    763	.cmd = NLBL_MGMT_C_ADD,
    764	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    765	.flags = GENL_ADMIN_PERM,
    766	.doit = netlbl_mgmt_add,
    767	.dumpit = NULL,
    768	},
    769	{
    770	.cmd = NLBL_MGMT_C_REMOVE,
    771	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    772	.flags = GENL_ADMIN_PERM,
    773	.doit = netlbl_mgmt_remove,
    774	.dumpit = NULL,
    775	},
    776	{
    777	.cmd = NLBL_MGMT_C_LISTALL,
    778	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    779	.flags = 0,
    780	.doit = NULL,
    781	.dumpit = netlbl_mgmt_listall,
    782	},
    783	{
    784	.cmd = NLBL_MGMT_C_ADDDEF,
    785	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    786	.flags = GENL_ADMIN_PERM,
    787	.doit = netlbl_mgmt_adddef,
    788	.dumpit = NULL,
    789	},
    790	{
    791	.cmd = NLBL_MGMT_C_REMOVEDEF,
    792	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    793	.flags = GENL_ADMIN_PERM,
    794	.doit = netlbl_mgmt_removedef,
    795	.dumpit = NULL,
    796	},
    797	{
    798	.cmd = NLBL_MGMT_C_LISTDEF,
    799	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    800	.flags = 0,
    801	.doit = netlbl_mgmt_listdef,
    802	.dumpit = NULL,
    803	},
    804	{
    805	.cmd = NLBL_MGMT_C_PROTOCOLS,
    806	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    807	.flags = 0,
    808	.doit = NULL,
    809	.dumpit = netlbl_mgmt_protocols,
    810	},
    811	{
    812	.cmd = NLBL_MGMT_C_VERSION,
    813	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    814	.flags = 0,
    815	.doit = netlbl_mgmt_version,
    816	.dumpit = NULL,
    817	},
    818};
    819
    820static struct genl_family netlbl_mgmt_gnl_family __ro_after_init = {
    821	.hdrsize = 0,
    822	.name = NETLBL_NLTYPE_MGMT_NAME,
    823	.version = NETLBL_PROTO_VERSION,
    824	.maxattr = NLBL_MGMT_A_MAX,
    825	.policy = netlbl_mgmt_genl_policy,
    826	.module = THIS_MODULE,
    827	.small_ops = netlbl_mgmt_genl_ops,
    828	.n_small_ops = ARRAY_SIZE(netlbl_mgmt_genl_ops),
    829};
    830
    831/*
    832 * NetLabel Generic NETLINK Protocol Functions
    833 */
    834
    835/**
    836 * netlbl_mgmt_genl_init - Register the NetLabel management component
    837 *
    838 * Description:
    839 * Register the NetLabel management component with the Generic NETLINK
    840 * mechanism.  Returns zero on success, negative values on failure.
    841 *
    842 */
    843int __init netlbl_mgmt_genl_init(void)
    844{
    845	return genl_register_family(&netlbl_mgmt_gnl_family);
    846}