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