1 /*
2  * NetLabel CIPSO/IPv4 Support
3  *
4  * This file defines the CIPSO/IPv4 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
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/audit.h>
35 #include <linux/slab.h>
36 #include <net/sock.h>
37 #include <net/netlink.h>
38 #include <net/genetlink.h>
39 #include <net/netlabel.h>
40 #include <net/cipso_ipv4.h>
41 #include <linux/atomic.h>
42 
43 #include "netlabel_user.h"
44 #include "netlabel_cipso_v4.h"
45 #include "netlabel_mgmt.h"
46 #include "netlabel_domainhash.h"
47 
48 /* Argument struct for cipso_v4_doi_walk() */
49 struct netlbl_cipsov4_doiwalk_arg {
50 	struct netlink_callback *nl_cb;
51 	struct sk_buff *skb;
52 	u32 seq;
53 };
54 
55 /* Argument struct for netlbl_domhsh_walk() */
56 struct netlbl_domhsh_walk_arg {
57 	struct netlbl_audit *audit_info;
58 	u32 doi;
59 };
60 
61 /* NetLabel Generic NETLINK CIPSOv4 family */
62 static struct genl_family netlbl_cipsov4_gnl_family;
63 /* NetLabel Netlink attribute policy */
64 static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1] = {
65 	[NLBL_CIPSOV4_A_DOI] = { .type = NLA_U32 },
66 	[NLBL_CIPSOV4_A_MTYPE] = { .type = NLA_U32 },
67 	[NLBL_CIPSOV4_A_TAG] = { .type = NLA_U8 },
68 	[NLBL_CIPSOV4_A_TAGLST] = { .type = NLA_NESTED },
69 	[NLBL_CIPSOV4_A_MLSLVLLOC] = { .type = NLA_U32 },
70 	[NLBL_CIPSOV4_A_MLSLVLREM] = { .type = NLA_U32 },
71 	[NLBL_CIPSOV4_A_MLSLVL] = { .type = NLA_NESTED },
72 	[NLBL_CIPSOV4_A_MLSLVLLST] = { .type = NLA_NESTED },
73 	[NLBL_CIPSOV4_A_MLSCATLOC] = { .type = NLA_U32 },
74 	[NLBL_CIPSOV4_A_MLSCATREM] = { .type = NLA_U32 },
75 	[NLBL_CIPSOV4_A_MLSCAT] = { .type = NLA_NESTED },
76 	[NLBL_CIPSOV4_A_MLSCATLST] = { .type = NLA_NESTED },
77 };
78 
79 /*
80  * Helper Functions
81  */
82 
83 /**
84  * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
85  * @info: the Generic NETLINK info block
86  * @doi_def: the CIPSO V4 DOI definition
87  *
88  * Description:
89  * Parse the common sections of a ADD message and fill in the related values
90  * in @doi_def.  Returns zero on success, negative values on failure.
91  *
92  */
93 static int netlbl_cipsov4_add_common(struct genl_info *info,
94 				     struct cipso_v4_doi *doi_def)
95 {
96 	struct nlattr *nla;
97 	int nla_rem;
98 	u32 iter = 0;
99 
100 	doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
101 
102 	if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST],
103 				NLBL_CIPSOV4_A_MAX,
104 				netlbl_cipsov4_genl_policy, NULL) != 0)
105 		return -EINVAL;
106 
107 	nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem)
108 		if (nla_type(nla) == NLBL_CIPSOV4_A_TAG) {
109 			if (iter >= CIPSO_V4_TAG_MAXCNT)
110 				return -EINVAL;
111 			doi_def->tags[iter++] = nla_get_u8(nla);
112 		}
113 	while (iter < CIPSO_V4_TAG_MAXCNT)
114 		doi_def->tags[iter++] = CIPSO_V4_TAG_INVALID;
115 
116 	return 0;
117 }
118 
119 /*
120  * NetLabel Command Handlers
121  */
122 
123 /**
124  * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
125  * @info: the Generic NETLINK info block
126  * @audit_info: NetLabel audit information
127  *
128  * Description:
129  * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD
130  * message and add it to the CIPSO V4 engine.  Return zero on success and
131  * non-zero on error.
132  *
133  */
134 static int netlbl_cipsov4_add_std(struct genl_info *info,
135 				  struct netlbl_audit *audit_info)
136 {
137 	int ret_val = -EINVAL;
138 	struct cipso_v4_doi *doi_def = NULL;
139 	struct nlattr *nla_a;
140 	struct nlattr *nla_b;
141 	int nla_a_rem;
142 	int nla_b_rem;
143 	u32 iter;
144 
145 	if (!info->attrs[NLBL_CIPSOV4_A_TAGLST] ||
146 	    !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST])
147 		return -EINVAL;
148 
149 	if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
150 				NLBL_CIPSOV4_A_MAX,
151 				netlbl_cipsov4_genl_policy, NULL) != 0)
152 		return -EINVAL;
153 
154 	doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
155 	if (doi_def == NULL)
156 		return -ENOMEM;
157 	doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL);
158 	if (doi_def->map.std == NULL) {
159 		ret_val = -ENOMEM;
160 		goto add_std_failure;
161 	}
162 	doi_def->type = CIPSO_V4_MAP_TRANS;
163 
164 	ret_val = netlbl_cipsov4_add_common(info, doi_def);
165 	if (ret_val != 0)
166 		goto add_std_failure;
167 	ret_val = -EINVAL;
168 
169 	nla_for_each_nested(nla_a,
170 			    info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
171 			    nla_a_rem)
172 		if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
173 			if (nla_validate_nested(nla_a, NLBL_CIPSOV4_A_MAX,
174 						netlbl_cipsov4_genl_policy,
175 						NULL) != 0)
176 				goto add_std_failure;
177 			nla_for_each_nested(nla_b, nla_a, nla_b_rem)
178 				switch (nla_type(nla_b)) {
179 				case NLBL_CIPSOV4_A_MLSLVLLOC:
180 					if (nla_get_u32(nla_b) >
181 					    CIPSO_V4_MAX_LOC_LVLS)
182 						goto add_std_failure;
183 					if (nla_get_u32(nla_b) >=
184 					    doi_def->map.std->lvl.local_size)
185 					     doi_def->map.std->lvl.local_size =
186 						     nla_get_u32(nla_b) + 1;
187 					break;
188 				case NLBL_CIPSOV4_A_MLSLVLREM:
189 					if (nla_get_u32(nla_b) >
190 					    CIPSO_V4_MAX_REM_LVLS)
191 						goto add_std_failure;
192 					if (nla_get_u32(nla_b) >=
193 					    doi_def->map.std->lvl.cipso_size)
194 					     doi_def->map.std->lvl.cipso_size =
195 						     nla_get_u32(nla_b) + 1;
196 					break;
197 				}
198 		}
199 	doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size,
200 					      sizeof(u32),
201 					      GFP_KERNEL);
202 	if (doi_def->map.std->lvl.local == NULL) {
203 		ret_val = -ENOMEM;
204 		goto add_std_failure;
205 	}
206 	doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size,
207 					      sizeof(u32),
208 					      GFP_KERNEL);
209 	if (doi_def->map.std->lvl.cipso == NULL) {
210 		ret_val = -ENOMEM;
211 		goto add_std_failure;
212 	}
213 	for (iter = 0; iter < doi_def->map.std->lvl.local_size; iter++)
214 		doi_def->map.std->lvl.local[iter] = CIPSO_V4_INV_LVL;
215 	for (iter = 0; iter < doi_def->map.std->lvl.cipso_size; iter++)
216 		doi_def->map.std->lvl.cipso[iter] = CIPSO_V4_INV_LVL;
217 	nla_for_each_nested(nla_a,
218 			    info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
219 			    nla_a_rem)
220 		if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
221 			struct nlattr *lvl_loc;
222 			struct nlattr *lvl_rem;
223 
224 			lvl_loc = nla_find_nested(nla_a,
225 						  NLBL_CIPSOV4_A_MLSLVLLOC);
226 			lvl_rem = nla_find_nested(nla_a,
227 						  NLBL_CIPSOV4_A_MLSLVLREM);
228 			if (lvl_loc == NULL || lvl_rem == NULL)
229 				goto add_std_failure;
230 			doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] =
231 				nla_get_u32(lvl_rem);
232 			doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] =
233 				nla_get_u32(lvl_loc);
234 		}
235 
236 	if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) {
237 		if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
238 					NLBL_CIPSOV4_A_MAX,
239 					netlbl_cipsov4_genl_policy, NULL) != 0)
240 			goto add_std_failure;
241 
242 		nla_for_each_nested(nla_a,
243 				    info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
244 				    nla_a_rem)
245 			if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
246 				if (nla_validate_nested(nla_a,
247 							NLBL_CIPSOV4_A_MAX,
248 							netlbl_cipsov4_genl_policy,
249 							NULL) != 0)
250 					goto add_std_failure;
251 				nla_for_each_nested(nla_b, nla_a, nla_b_rem)
252 					switch (nla_type(nla_b)) {
253 					case NLBL_CIPSOV4_A_MLSCATLOC:
254 						if (nla_get_u32(nla_b) >
255 						    CIPSO_V4_MAX_LOC_CATS)
256 							goto add_std_failure;
257 						if (nla_get_u32(nla_b) >=
258 					      doi_def->map.std->cat.local_size)
259 					     doi_def->map.std->cat.local_size =
260 						     nla_get_u32(nla_b) + 1;
261 						break;
262 					case NLBL_CIPSOV4_A_MLSCATREM:
263 						if (nla_get_u32(nla_b) >
264 						    CIPSO_V4_MAX_REM_CATS)
265 							goto add_std_failure;
266 						if (nla_get_u32(nla_b) >=
267 					      doi_def->map.std->cat.cipso_size)
268 					     doi_def->map.std->cat.cipso_size =
269 						     nla_get_u32(nla_b) + 1;
270 						break;
271 					}
272 			}
273 		doi_def->map.std->cat.local = kcalloc(
274 					      doi_def->map.std->cat.local_size,
275 					      sizeof(u32),
276 					      GFP_KERNEL);
277 		if (doi_def->map.std->cat.local == NULL) {
278 			ret_val = -ENOMEM;
279 			goto add_std_failure;
280 		}
281 		doi_def->map.std->cat.cipso = kcalloc(
282 					      doi_def->map.std->cat.cipso_size,
283 					      sizeof(u32),
284 					      GFP_KERNEL);
285 		if (doi_def->map.std->cat.cipso == NULL) {
286 			ret_val = -ENOMEM;
287 			goto add_std_failure;
288 		}
289 		for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++)
290 			doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT;
291 		for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++)
292 			doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT;
293 		nla_for_each_nested(nla_a,
294 				    info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
295 				    nla_a_rem)
296 			if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
297 				struct nlattr *cat_loc;
298 				struct nlattr *cat_rem;
299 
300 				cat_loc = nla_find_nested(nla_a,
301 						     NLBL_CIPSOV4_A_MLSCATLOC);
302 				cat_rem = nla_find_nested(nla_a,
303 						     NLBL_CIPSOV4_A_MLSCATREM);
304 				if (cat_loc == NULL || cat_rem == NULL)
305 					goto add_std_failure;
306 				doi_def->map.std->cat.local[
307 							nla_get_u32(cat_loc)] =
308 					nla_get_u32(cat_rem);
309 				doi_def->map.std->cat.cipso[
310 							nla_get_u32(cat_rem)] =
311 					nla_get_u32(cat_loc);
312 			}
313 	}
314 
315 	ret_val = cipso_v4_doi_add(doi_def, audit_info);
316 	if (ret_val != 0)
317 		goto add_std_failure;
318 	return 0;
319 
320 add_std_failure:
321 	cipso_v4_doi_free(doi_def);
322 	return ret_val;
323 }
324 
325 /**
326  * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
327  * @info: the Generic NETLINK info block
328  * @audit_info: NetLabel audit information
329  *
330  * Description:
331  * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
332  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
333  * error.
334  *
335  */
336 static int netlbl_cipsov4_add_pass(struct genl_info *info,
337 				   struct netlbl_audit *audit_info)
338 {
339 	int ret_val;
340 	struct cipso_v4_doi *doi_def = NULL;
341 
342 	if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
343 		return -EINVAL;
344 
345 	doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
346 	if (doi_def == NULL)
347 		return -ENOMEM;
348 	doi_def->type = CIPSO_V4_MAP_PASS;
349 
350 	ret_val = netlbl_cipsov4_add_common(info, doi_def);
351 	if (ret_val != 0)
352 		goto add_pass_failure;
353 
354 	ret_val = cipso_v4_doi_add(doi_def, audit_info);
355 	if (ret_val != 0)
356 		goto add_pass_failure;
357 	return 0;
358 
359 add_pass_failure:
360 	cipso_v4_doi_free(doi_def);
361 	return ret_val;
362 }
363 
364 /**
365  * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
366  * @info: the Generic NETLINK info block
367  * @audit_info: NetLabel audit information
368  *
369  * Description:
370  * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
371  * message and add it to the CIPSO V4 engine.  Return zero on success and
372  * non-zero on error.
373  *
374  */
375 static int netlbl_cipsov4_add_local(struct genl_info *info,
376 				    struct netlbl_audit *audit_info)
377 {
378 	int ret_val;
379 	struct cipso_v4_doi *doi_def = NULL;
380 
381 	if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
382 		return -EINVAL;
383 
384 	doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
385 	if (doi_def == NULL)
386 		return -ENOMEM;
387 	doi_def->type = CIPSO_V4_MAP_LOCAL;
388 
389 	ret_val = netlbl_cipsov4_add_common(info, doi_def);
390 	if (ret_val != 0)
391 		goto add_local_failure;
392 
393 	ret_val = cipso_v4_doi_add(doi_def, audit_info);
394 	if (ret_val != 0)
395 		goto add_local_failure;
396 	return 0;
397 
398 add_local_failure:
399 	cipso_v4_doi_free(doi_def);
400 	return ret_val;
401 }
402 
403 /**
404  * netlbl_cipsov4_add - Handle an ADD message
405  * @skb: the NETLINK buffer
406  * @info: the Generic NETLINK info block
407  *
408  * Description:
409  * Create a new DOI definition based on the given ADD message and add it to the
410  * CIPSO V4 engine.  Returns zero on success, negative values on failure.
411  *
412  */
413 static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
414 
415 {
416 	int ret_val = -EINVAL;
417 	struct netlbl_audit audit_info;
418 
419 	if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
420 	    !info->attrs[NLBL_CIPSOV4_A_MTYPE])
421 		return -EINVAL;
422 
423 	netlbl_netlink_auditinfo(skb, &audit_info);
424 	switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {
425 	case CIPSO_V4_MAP_TRANS:
426 		ret_val = netlbl_cipsov4_add_std(info, &audit_info);
427 		break;
428 	case CIPSO_V4_MAP_PASS:
429 		ret_val = netlbl_cipsov4_add_pass(info, &audit_info);
430 		break;
431 	case CIPSO_V4_MAP_LOCAL:
432 		ret_val = netlbl_cipsov4_add_local(info, &audit_info);
433 		break;
434 	}
435 	if (ret_val == 0)
436 		atomic_inc(&netlabel_mgmt_protocount);
437 
438 	return ret_val;
439 }
440 
441 /**
442  * netlbl_cipsov4_list - Handle a LIST message
443  * @skb: the NETLINK buffer
444  * @info: the Generic NETLINK info block
445  *
446  * Description:
447  * Process a user generated LIST message and respond accordingly.  While the
448  * response message generated by the kernel is straightforward, determining
449  * before hand the size of the buffer to allocate is not (we have to generate
450  * the message to know the size).  In order to keep this function sane what we
451  * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in
452  * that size, if we fail then we restart with a larger buffer and try again.
453  * We continue in this manner until we hit a limit of failed attempts then we
454  * give up and just send an error message.  Returns zero on success and
455  * negative values on error.
456  *
457  */
458 static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
459 {
460 	int ret_val;
461 	struct sk_buff *ans_skb = NULL;
462 	u32 nlsze_mult = 1;
463 	void *data;
464 	u32 doi;
465 	struct nlattr *nla_a;
466 	struct nlattr *nla_b;
467 	struct cipso_v4_doi *doi_def;
468 	u32 iter;
469 
470 	if (!info->attrs[NLBL_CIPSOV4_A_DOI]) {
471 		ret_val = -EINVAL;
472 		goto list_failure;
473 	}
474 
475 list_start:
476 	ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE * nlsze_mult, GFP_KERNEL);
477 	if (ans_skb == NULL) {
478 		ret_val = -ENOMEM;
479 		goto list_failure;
480 	}
481 	data = genlmsg_put_reply(ans_skb, info, &netlbl_cipsov4_gnl_family,
482 				 0, NLBL_CIPSOV4_C_LIST);
483 	if (data == NULL) {
484 		ret_val = -ENOMEM;
485 		goto list_failure;
486 	}
487 
488 	doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
489 
490 	rcu_read_lock();
491 	doi_def = cipso_v4_doi_getdef(doi);
492 	if (doi_def == NULL) {
493 		ret_val = -EINVAL;
494 		goto list_failure_lock;
495 	}
496 
497 	ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
498 	if (ret_val != 0)
499 		goto list_failure_lock;
500 
501 	nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_TAGLST);
502 	if (nla_a == NULL) {
503 		ret_val = -ENOMEM;
504 		goto list_failure_lock;
505 	}
506 	for (iter = 0;
507 	     iter < CIPSO_V4_TAG_MAXCNT &&
508 	       doi_def->tags[iter] != CIPSO_V4_TAG_INVALID;
509 	     iter++) {
510 		ret_val = nla_put_u8(ans_skb,
511 				     NLBL_CIPSOV4_A_TAG,
512 				     doi_def->tags[iter]);
513 		if (ret_val != 0)
514 			goto list_failure_lock;
515 	}
516 	nla_nest_end(ans_skb, nla_a);
517 
518 	switch (doi_def->type) {
519 	case CIPSO_V4_MAP_TRANS:
520 		nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
521 		if (nla_a == NULL) {
522 			ret_val = -ENOMEM;
523 			goto list_failure_lock;
524 		}
525 		for (iter = 0;
526 		     iter < doi_def->map.std->lvl.local_size;
527 		     iter++) {
528 			if (doi_def->map.std->lvl.local[iter] ==
529 			    CIPSO_V4_INV_LVL)
530 				continue;
531 
532 			nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVL);
533 			if (nla_b == NULL) {
534 				ret_val = -ENOMEM;
535 				goto list_retry;
536 			}
537 			ret_val = nla_put_u32(ans_skb,
538 					      NLBL_CIPSOV4_A_MLSLVLLOC,
539 					      iter);
540 			if (ret_val != 0)
541 				goto list_retry;
542 			ret_val = nla_put_u32(ans_skb,
543 					    NLBL_CIPSOV4_A_MLSLVLREM,
544 					    doi_def->map.std->lvl.local[iter]);
545 			if (ret_val != 0)
546 				goto list_retry;
547 			nla_nest_end(ans_skb, nla_b);
548 		}
549 		nla_nest_end(ans_skb, nla_a);
550 
551 		nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCATLST);
552 		if (nla_a == NULL) {
553 			ret_val = -ENOMEM;
554 			goto list_retry;
555 		}
556 		for (iter = 0;
557 		     iter < doi_def->map.std->cat.local_size;
558 		     iter++) {
559 			if (doi_def->map.std->cat.local[iter] ==
560 			    CIPSO_V4_INV_CAT)
561 				continue;
562 
563 			nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCAT);
564 			if (nla_b == NULL) {
565 				ret_val = -ENOMEM;
566 				goto list_retry;
567 			}
568 			ret_val = nla_put_u32(ans_skb,
569 					      NLBL_CIPSOV4_A_MLSCATLOC,
570 					      iter);
571 			if (ret_val != 0)
572 				goto list_retry;
573 			ret_val = nla_put_u32(ans_skb,
574 					    NLBL_CIPSOV4_A_MLSCATREM,
575 					    doi_def->map.std->cat.local[iter]);
576 			if (ret_val != 0)
577 				goto list_retry;
578 			nla_nest_end(ans_skb, nla_b);
579 		}
580 		nla_nest_end(ans_skb, nla_a);
581 
582 		break;
583 	}
584 	rcu_read_unlock();
585 
586 	genlmsg_end(ans_skb, data);
587 	return genlmsg_reply(ans_skb, info);
588 
589 list_retry:
590 	/* XXX - this limit is a guesstimate */
591 	if (nlsze_mult < 4) {
592 		rcu_read_unlock();
593 		kfree_skb(ans_skb);
594 		nlsze_mult *= 2;
595 		goto list_start;
596 	}
597 list_failure_lock:
598 	rcu_read_unlock();
599 list_failure:
600 	kfree_skb(ans_skb);
601 	return ret_val;
602 }
603 
604 /**
605  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
606  * @doi_def: the CIPSOv4 DOI definition
607  * @arg: the netlbl_cipsov4_doiwalk_arg structure
608  *
609  * Description:
610  * This function is designed to be used as a callback to the
611  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
612  * message.  Returns the size of the message on success, negative values on
613  * failure.
614  *
615  */
616 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
617 {
618 	int ret_val = -ENOMEM;
619 	struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
620 	void *data;
621 
622 	data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
623 			   cb_arg->seq, &netlbl_cipsov4_gnl_family,
624 			   NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
625 	if (data == NULL)
626 		goto listall_cb_failure;
627 
628 	ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
629 	if (ret_val != 0)
630 		goto listall_cb_failure;
631 	ret_val = nla_put_u32(cb_arg->skb,
632 			      NLBL_CIPSOV4_A_MTYPE,
633 			      doi_def->type);
634 	if (ret_val != 0)
635 		goto listall_cb_failure;
636 
637 	genlmsg_end(cb_arg->skb, data);
638 	return 0;
639 
640 listall_cb_failure:
641 	genlmsg_cancel(cb_arg->skb, data);
642 	return ret_val;
643 }
644 
645 /**
646  * netlbl_cipsov4_listall - Handle a LISTALL message
647  * @skb: the NETLINK buffer
648  * @cb: the NETLINK callback
649  *
650  * Description:
651  * Process a user generated LISTALL message and respond accordingly.  Returns
652  * zero on success and negative values on error.
653  *
654  */
655 static int netlbl_cipsov4_listall(struct sk_buff *skb,
656 				  struct netlink_callback *cb)
657 {
658 	struct netlbl_cipsov4_doiwalk_arg cb_arg;
659 	u32 doi_skip = cb->args[0];
660 
661 	cb_arg.nl_cb = cb;
662 	cb_arg.skb = skb;
663 	cb_arg.seq = cb->nlh->nlmsg_seq;
664 
665 	cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
666 
667 	cb->args[0] = doi_skip;
668 	return skb->len;
669 }
670 
671 /**
672  * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE
673  * @entry: LSM domain mapping entry
674  * @arg: the netlbl_domhsh_walk_arg structure
675  *
676  * Description:
677  * This function is intended for use by netlbl_cipsov4_remove() as the callback
678  * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
679  * which are associated with the CIPSO DOI specified in @arg.  Returns zero on
680  * success, negative values on failure.
681  *
682  */
683 static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
684 {
685 	struct netlbl_domhsh_walk_arg *cb_arg = arg;
686 
687 	if (entry->def.type == NETLBL_NLTYPE_CIPSOV4 &&
688 	    entry->def.cipso->doi == cb_arg->doi)
689 		return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
690 
691 	return 0;
692 }
693 
694 /**
695  * netlbl_cipsov4_remove - Handle a REMOVE message
696  * @skb: the NETLINK buffer
697  * @info: the Generic NETLINK info block
698  *
699  * Description:
700  * Process a user generated REMOVE message and respond accordingly.  Returns
701  * zero on success, negative values on failure.
702  *
703  */
704 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
705 {
706 	int ret_val = -EINVAL;
707 	struct netlbl_domhsh_walk_arg cb_arg;
708 	struct netlbl_audit audit_info;
709 	u32 skip_bkt = 0;
710 	u32 skip_chain = 0;
711 
712 	if (!info->attrs[NLBL_CIPSOV4_A_DOI])
713 		return -EINVAL;
714 
715 	netlbl_netlink_auditinfo(skb, &audit_info);
716 	cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
717 	cb_arg.audit_info = &audit_info;
718 	ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
719 				     netlbl_cipsov4_remove_cb, &cb_arg);
720 	if (ret_val == 0 || ret_val == -ENOENT) {
721 		ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info);
722 		if (ret_val == 0)
723 			atomic_dec(&netlabel_mgmt_protocount);
724 	}
725 
726 	return ret_val;
727 }
728 
729 /*
730  * NetLabel Generic NETLINK Command Definitions
731  */
732 
733 static const struct genl_ops netlbl_cipsov4_ops[] = {
734 	{
735 	.cmd = NLBL_CIPSOV4_C_ADD,
736 	.flags = GENL_ADMIN_PERM,
737 	.policy = netlbl_cipsov4_genl_policy,
738 	.doit = netlbl_cipsov4_add,
739 	.dumpit = NULL,
740 	},
741 	{
742 	.cmd = NLBL_CIPSOV4_C_REMOVE,
743 	.flags = GENL_ADMIN_PERM,
744 	.policy = netlbl_cipsov4_genl_policy,
745 	.doit = netlbl_cipsov4_remove,
746 	.dumpit = NULL,
747 	},
748 	{
749 	.cmd = NLBL_CIPSOV4_C_LIST,
750 	.flags = 0,
751 	.policy = netlbl_cipsov4_genl_policy,
752 	.doit = netlbl_cipsov4_list,
753 	.dumpit = NULL,
754 	},
755 	{
756 	.cmd = NLBL_CIPSOV4_C_LISTALL,
757 	.flags = 0,
758 	.policy = netlbl_cipsov4_genl_policy,
759 	.doit = NULL,
760 	.dumpit = netlbl_cipsov4_listall,
761 	},
762 };
763 
764 static struct genl_family netlbl_cipsov4_gnl_family __ro_after_init = {
765 	.hdrsize = 0,
766 	.name = NETLBL_NLTYPE_CIPSOV4_NAME,
767 	.version = NETLBL_PROTO_VERSION,
768 	.maxattr = NLBL_CIPSOV4_A_MAX,
769 	.module = THIS_MODULE,
770 	.ops = netlbl_cipsov4_ops,
771 	.n_ops = ARRAY_SIZE(netlbl_cipsov4_ops),
772 };
773 
774 /*
775  * NetLabel Generic NETLINK Protocol Functions
776  */
777 
778 /**
779  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
780  *
781  * Description:
782  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
783  * mechanism.  Returns zero on success, negative values on failure.
784  *
785  */
786 int __init netlbl_cipsov4_genl_init(void)
787 {
788 	return genl_register_family(&netlbl_cipsov4_gnl_family);
789 }
790