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