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