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