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