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 #include <asm/atomic.h>
42 
43 #include "netlabel_user.h"
44 #include "netlabel_cipso_v4.h"
45 #include "netlabel_mgmt.h"
46 
47 /* Argument struct for cipso_v4_doi_walk() */
48 struct netlbl_cipsov4_doiwalk_arg {
49 	struct netlink_callback *nl_cb;
50 	struct sk_buff *skb;
51 	u32 seq;
52 };
53 
54 /* NetLabel Generic NETLINK CIPSOv4 family */
55 static struct genl_family netlbl_cipsov4_gnl_family = {
56 	.id = GENL_ID_GENERATE,
57 	.hdrsize = 0,
58 	.name = NETLBL_NLTYPE_CIPSOV4_NAME,
59 	.version = NETLBL_PROTO_VERSION,
60 	.maxattr = NLBL_CIPSOV4_A_MAX,
61 };
62 
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_doi_free - Frees a CIPSO V4 DOI definition
85  * @entry: the entry's RCU field
86  *
87  * Description:
88  * This function is designed to be used as a callback to the call_rcu()
89  * function so that the memory allocated to the DOI definition can be released
90  * safely.
91  *
92  */
93 static void netlbl_cipsov4_doi_free(struct rcu_head *entry)
94 {
95 	struct cipso_v4_doi *ptr;
96 
97 	ptr = container_of(entry, struct cipso_v4_doi, rcu);
98 	switch (ptr->type) {
99 	case CIPSO_V4_MAP_STD:
100 		kfree(ptr->map.std->lvl.cipso);
101 		kfree(ptr->map.std->lvl.local);
102 		kfree(ptr->map.std->cat.cipso);
103 		kfree(ptr->map.std->cat.local);
104 		break;
105 	}
106 	kfree(ptr);
107 }
108 
109 /**
110  * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
111  * @info: the Generic NETLINK info block
112  * @doi_def: the CIPSO V4 DOI definition
113  *
114  * Description:
115  * Parse the common sections of a ADD message and fill in the related values
116  * in @doi_def.  Returns zero on success, negative values on failure.
117  *
118  */
119 static int netlbl_cipsov4_add_common(struct genl_info *info,
120 				     struct cipso_v4_doi *doi_def)
121 {
122 	struct nlattr *nla;
123 	int nla_rem;
124 	u32 iter = 0;
125 
126 	doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
127 
128 	if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST],
129 				NLBL_CIPSOV4_A_MAX,
130 				netlbl_cipsov4_genl_policy) != 0)
131 		return -EINVAL;
132 
133 	nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem)
134 		if (nla_type(nla) == NLBL_CIPSOV4_A_TAG) {
135 			if (iter >= CIPSO_V4_TAG_MAXCNT)
136 				return -EINVAL;
137 			doi_def->tags[iter++] = nla_get_u8(nla);
138 		}
139 	while (iter < CIPSO_V4_TAG_MAXCNT)
140 		doi_def->tags[iter++] = CIPSO_V4_TAG_INVALID;
141 
142 	return 0;
143 }
144 
145 /*
146  * NetLabel Command Handlers
147  */
148 
149 /**
150  * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
151  * @info: the Generic NETLINK info block
152  *
153  * Description:
154  * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message
155  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
156  * error.
157  *
158  */
159 static int netlbl_cipsov4_add_std(struct genl_info *info)
160 {
161 	int ret_val = -EINVAL;
162 	struct cipso_v4_doi *doi_def = NULL;
163 	struct nlattr *nla_a;
164 	struct nlattr *nla_b;
165 	int nla_a_rem;
166 	int nla_b_rem;
167 	u32 iter;
168 
169 	if (!info->attrs[NLBL_CIPSOV4_A_TAGLST] ||
170 	    !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST])
171 		return -EINVAL;
172 
173 	if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
174 				NLBL_CIPSOV4_A_MAX,
175 				netlbl_cipsov4_genl_policy) != 0)
176 		return -EINVAL;
177 
178 	doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
179 	if (doi_def == NULL)
180 		return -ENOMEM;
181 	doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL);
182 	if (doi_def->map.std == NULL) {
183 		ret_val = -ENOMEM;
184 		goto add_std_failure;
185 	}
186 	doi_def->type = CIPSO_V4_MAP_STD;
187 
188 	ret_val = netlbl_cipsov4_add_common(info, doi_def);
189 	if (ret_val != 0)
190 		goto add_std_failure;
191 	ret_val = -EINVAL;
192 
193 	nla_for_each_nested(nla_a,
194 			    info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
195 			    nla_a_rem)
196 		if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
197 			if (nla_validate_nested(nla_a,
198 					    NLBL_CIPSOV4_A_MAX,
199 					    netlbl_cipsov4_genl_policy) != 0)
200 					goto add_std_failure;
201 			nla_for_each_nested(nla_b, nla_a, nla_b_rem)
202 				switch (nla_type(nla_b)) {
203 				case NLBL_CIPSOV4_A_MLSLVLLOC:
204 					if (nla_get_u32(nla_b) >
205 					    CIPSO_V4_MAX_LOC_LVLS)
206 						goto add_std_failure;
207 					if (nla_get_u32(nla_b) >=
208 					    doi_def->map.std->lvl.local_size)
209 					     doi_def->map.std->lvl.local_size =
210 						     nla_get_u32(nla_b) + 1;
211 					break;
212 				case NLBL_CIPSOV4_A_MLSLVLREM:
213 					if (nla_get_u32(nla_b) >
214 					    CIPSO_V4_MAX_REM_LVLS)
215 						goto add_std_failure;
216 					if (nla_get_u32(nla_b) >=
217 					    doi_def->map.std->lvl.cipso_size)
218 					     doi_def->map.std->lvl.cipso_size =
219 						     nla_get_u32(nla_b) + 1;
220 					break;
221 				}
222 		}
223 	doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size,
224 					      sizeof(u32),
225 					      GFP_KERNEL);
226 	if (doi_def->map.std->lvl.local == NULL) {
227 		ret_val = -ENOMEM;
228 		goto add_std_failure;
229 	}
230 	doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size,
231 					      sizeof(u32),
232 					      GFP_KERNEL);
233 	if (doi_def->map.std->lvl.cipso == NULL) {
234 		ret_val = -ENOMEM;
235 		goto add_std_failure;
236 	}
237 	for (iter = 0; iter < doi_def->map.std->lvl.local_size; iter++)
238 		doi_def->map.std->lvl.local[iter] = CIPSO_V4_INV_LVL;
239 	for (iter = 0; iter < doi_def->map.std->lvl.cipso_size; iter++)
240 		doi_def->map.std->lvl.cipso[iter] = CIPSO_V4_INV_LVL;
241 	nla_for_each_nested(nla_a,
242 			    info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
243 			    nla_a_rem)
244 		if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
245 			struct nlattr *lvl_loc;
246 			struct nlattr *lvl_rem;
247 
248 			lvl_loc = nla_find_nested(nla_a,
249 						  NLBL_CIPSOV4_A_MLSLVLLOC);
250 			lvl_rem = nla_find_nested(nla_a,
251 						  NLBL_CIPSOV4_A_MLSLVLREM);
252 			if (lvl_loc == NULL || lvl_rem == NULL)
253 				goto add_std_failure;
254 			doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] =
255 				nla_get_u32(lvl_rem);
256 			doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] =
257 				nla_get_u32(lvl_loc);
258 		}
259 
260 	if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) {
261 		if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
262 					NLBL_CIPSOV4_A_MAX,
263 					netlbl_cipsov4_genl_policy) != 0)
264 			goto add_std_failure;
265 
266 		nla_for_each_nested(nla_a,
267 				    info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
268 				    nla_a_rem)
269 			if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
270 				if (nla_validate_nested(nla_a,
271 					      NLBL_CIPSOV4_A_MAX,
272 					      netlbl_cipsov4_genl_policy) != 0)
273 					goto add_std_failure;
274 				nla_for_each_nested(nla_b, nla_a, nla_b_rem)
275 					switch (nla_type(nla_b)) {
276 					case NLBL_CIPSOV4_A_MLSCATLOC:
277 						if (nla_get_u32(nla_b) >
278 						    CIPSO_V4_MAX_LOC_CATS)
279 							goto add_std_failure;
280 						if (nla_get_u32(nla_b) >=
281 					      doi_def->map.std->cat.local_size)
282 					     doi_def->map.std->cat.local_size =
283 						     nla_get_u32(nla_b) + 1;
284 						break;
285 					case NLBL_CIPSOV4_A_MLSCATREM:
286 						if (nla_get_u32(nla_b) >
287 						    CIPSO_V4_MAX_REM_CATS)
288 							goto add_std_failure;
289 						if (nla_get_u32(nla_b) >=
290 					      doi_def->map.std->cat.cipso_size)
291 					     doi_def->map.std->cat.cipso_size =
292 						     nla_get_u32(nla_b) + 1;
293 						break;
294 					}
295 			}
296 		doi_def->map.std->cat.local = kcalloc(
297 					      doi_def->map.std->cat.local_size,
298 					      sizeof(u32),
299 					      GFP_KERNEL);
300 		if (doi_def->map.std->cat.local == NULL) {
301 			ret_val = -ENOMEM;
302 			goto add_std_failure;
303 		}
304 		doi_def->map.std->cat.cipso = kcalloc(
305 					      doi_def->map.std->cat.cipso_size,
306 					      sizeof(u32),
307 					      GFP_KERNEL);
308 		if (doi_def->map.std->cat.cipso == NULL) {
309 			ret_val = -ENOMEM;
310 			goto add_std_failure;
311 		}
312 		for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++)
313 			doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT;
314 		for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++)
315 			doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT;
316 		nla_for_each_nested(nla_a,
317 				    info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
318 				    nla_a_rem)
319 			if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
320 				struct nlattr *cat_loc;
321 				struct nlattr *cat_rem;
322 
323 				cat_loc = nla_find_nested(nla_a,
324 						     NLBL_CIPSOV4_A_MLSCATLOC);
325 				cat_rem = nla_find_nested(nla_a,
326 						     NLBL_CIPSOV4_A_MLSCATREM);
327 				if (cat_loc == NULL || cat_rem == NULL)
328 					goto add_std_failure;
329 				doi_def->map.std->cat.local[
330 							nla_get_u32(cat_loc)] =
331 					nla_get_u32(cat_rem);
332 				doi_def->map.std->cat.cipso[
333 							nla_get_u32(cat_rem)] =
334 					nla_get_u32(cat_loc);
335 			}
336 	}
337 
338 	ret_val = cipso_v4_doi_add(doi_def);
339 	if (ret_val != 0)
340 		goto add_std_failure;
341 	return 0;
342 
343 add_std_failure:
344 	if (doi_def)
345 		netlbl_cipsov4_doi_free(&doi_def->rcu);
346 	return ret_val;
347 }
348 
349 /**
350  * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
351  * @info: the Generic NETLINK info block
352  *
353  * Description:
354  * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
355  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
356  * error.
357  *
358  */
359 static int netlbl_cipsov4_add_pass(struct genl_info *info)
360 {
361 	int ret_val;
362 	struct cipso_v4_doi *doi_def = NULL;
363 
364 	if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
365 		return -EINVAL;
366 
367 	doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
368 	if (doi_def == NULL)
369 		return -ENOMEM;
370 	doi_def->type = CIPSO_V4_MAP_PASS;
371 
372 	ret_val = netlbl_cipsov4_add_common(info, doi_def);
373 	if (ret_val != 0)
374 		goto add_pass_failure;
375 
376 	ret_val = cipso_v4_doi_add(doi_def);
377 	if (ret_val != 0)
378 		goto add_pass_failure;
379 	return 0;
380 
381 add_pass_failure:
382 	netlbl_cipsov4_doi_free(&doi_def->rcu);
383 	return ret_val;
384 }
385 
386 /**
387  * netlbl_cipsov4_add - Handle an ADD message
388  * @skb: the NETLINK buffer
389  * @info: the Generic NETLINK info block
390  *
391  * Description:
392  * Create a new DOI definition based on the given ADD message and add it to the
393  * CIPSO V4 engine.  Returns zero on success, negative values on failure.
394  *
395  */
396 static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
397 
398 {
399 	int ret_val = -EINVAL;
400 	u32 type;
401 	u32 doi;
402 	const char *type_str = "(unknown)";
403 	struct audit_buffer *audit_buf;
404 	struct netlbl_audit audit_info;
405 
406 	if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
407 	    !info->attrs[NLBL_CIPSOV4_A_MTYPE])
408 		return -EINVAL;
409 
410 	doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
411 	netlbl_netlink_auditinfo(skb, &audit_info);
412 
413 	type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
414 	switch (type) {
415 	case CIPSO_V4_MAP_STD:
416 		type_str = "std";
417 		ret_val = netlbl_cipsov4_add_std(info);
418 		break;
419 	case CIPSO_V4_MAP_PASS:
420 		type_str = "pass";
421 		ret_val = netlbl_cipsov4_add_pass(info);
422 		break;
423 	}
424 	if (ret_val == 0)
425 		atomic_inc(&netlabel_mgmt_protocount);
426 
427 	audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
428 					      &audit_info);
429 	if (audit_buf != NULL) {
430 		audit_log_format(audit_buf,
431 				 " cipso_doi=%u cipso_type=%s res=%u",
432 				 doi,
433 				 type_str,
434 				 ret_val == 0 ? 1 : 0);
435 		audit_log_end(audit_buf);
436 	}
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;
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_STD:
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 
588 	ret_val = genlmsg_reply(ans_skb, info);
589 	if (ret_val != 0)
590 		goto list_failure;
591 
592 	return 0;
593 
594 list_retry:
595 	/* XXX - this limit is a guesstimate */
596 	if (nlsze_mult < 4) {
597 		rcu_read_unlock();
598 		kfree_skb(ans_skb);
599 		nlsze_mult++;
600 		goto list_start;
601 	}
602 list_failure_lock:
603 	rcu_read_unlock();
604 list_failure:
605 	kfree_skb(ans_skb);
606 	return ret_val;
607 }
608 
609 /**
610  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
611  * @doi_def: the CIPSOv4 DOI definition
612  * @arg: the netlbl_cipsov4_doiwalk_arg structure
613  *
614  * Description:
615  * This function is designed to be used as a callback to the
616  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
617  * message.  Returns the size of the message on success, negative values on
618  * failure.
619  *
620  */
621 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
622 {
623 	int ret_val = -ENOMEM;
624 	struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
625 	void *data;
626 
627 	data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid,
628 			   cb_arg->seq, &netlbl_cipsov4_gnl_family,
629 			   NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
630 	if (data == NULL)
631 		goto listall_cb_failure;
632 
633 	ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
634 	if (ret_val != 0)
635 		goto listall_cb_failure;
636 	ret_val = nla_put_u32(cb_arg->skb,
637 			      NLBL_CIPSOV4_A_MTYPE,
638 			      doi_def->type);
639 	if (ret_val != 0)
640 		goto listall_cb_failure;
641 
642 	return genlmsg_end(cb_arg->skb, data);
643 
644 listall_cb_failure:
645 	genlmsg_cancel(cb_arg->skb, data);
646 	return ret_val;
647 }
648 
649 /**
650  * netlbl_cipsov4_listall - Handle a LISTALL message
651  * @skb: the NETLINK buffer
652  * @cb: the NETLINK callback
653  *
654  * Description:
655  * Process a user generated LISTALL message and respond accordingly.  Returns
656  * zero on success and negative values on error.
657  *
658  */
659 static int netlbl_cipsov4_listall(struct sk_buff *skb,
660 				  struct netlink_callback *cb)
661 {
662 	struct netlbl_cipsov4_doiwalk_arg cb_arg;
663 	int doi_skip = cb->args[0];
664 
665 	cb_arg.nl_cb = cb;
666 	cb_arg.skb = skb;
667 	cb_arg.seq = cb->nlh->nlmsg_seq;
668 
669 	cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
670 
671 	cb->args[0] = doi_skip;
672 	return skb->len;
673 }
674 
675 /**
676  * netlbl_cipsov4_remove - Handle a REMOVE message
677  * @skb: the NETLINK buffer
678  * @info: the Generic NETLINK info block
679  *
680  * Description:
681  * Process a user generated REMOVE message and respond accordingly.  Returns
682  * zero on success, negative values on failure.
683  *
684  */
685 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
686 {
687 	int ret_val = -EINVAL;
688 	u32 doi = 0;
689 	struct audit_buffer *audit_buf;
690 	struct netlbl_audit audit_info;
691 
692 	if (!info->attrs[NLBL_CIPSOV4_A_DOI])
693 		return -EINVAL;
694 
695 	doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
696 	netlbl_netlink_auditinfo(skb, &audit_info);
697 
698 	ret_val = cipso_v4_doi_remove(doi,
699 				      &audit_info,
700 				      netlbl_cipsov4_doi_free);
701 	if (ret_val == 0)
702 		atomic_dec(&netlabel_mgmt_protocount);
703 
704 	audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
705 					      &audit_info);
706 	if (audit_buf != NULL) {
707 		audit_log_format(audit_buf,
708 				 " cipso_doi=%u res=%u",
709 				 doi,
710 				 ret_val == 0 ? 1 : 0);
711 		audit_log_end(audit_buf);
712 	}
713 
714 	return ret_val;
715 }
716 
717 /*
718  * NetLabel Generic NETLINK Command Definitions
719  */
720 
721 static struct genl_ops netlbl_cipsov4_genl_c_add = {
722 	.cmd = NLBL_CIPSOV4_C_ADD,
723 	.flags = GENL_ADMIN_PERM,
724 	.policy = netlbl_cipsov4_genl_policy,
725 	.doit = netlbl_cipsov4_add,
726 	.dumpit = NULL,
727 };
728 
729 static struct genl_ops netlbl_cipsov4_genl_c_remove = {
730 	.cmd = NLBL_CIPSOV4_C_REMOVE,
731 	.flags = GENL_ADMIN_PERM,
732 	.policy = netlbl_cipsov4_genl_policy,
733 	.doit = netlbl_cipsov4_remove,
734 	.dumpit = NULL,
735 };
736 
737 static struct genl_ops netlbl_cipsov4_genl_c_list = {
738 	.cmd = NLBL_CIPSOV4_C_LIST,
739 	.flags = 0,
740 	.policy = netlbl_cipsov4_genl_policy,
741 	.doit = netlbl_cipsov4_list,
742 	.dumpit = NULL,
743 };
744 
745 static struct genl_ops netlbl_cipsov4_genl_c_listall = {
746 	.cmd = NLBL_CIPSOV4_C_LISTALL,
747 	.flags = 0,
748 	.policy = netlbl_cipsov4_genl_policy,
749 	.doit = NULL,
750 	.dumpit = netlbl_cipsov4_listall,
751 };
752 
753 /*
754  * NetLabel Generic NETLINK Protocol Functions
755  */
756 
757 /**
758  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
759  *
760  * Description:
761  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
762  * mechanism.  Returns zero on success, negative values on failure.
763  *
764  */
765 int netlbl_cipsov4_genl_init(void)
766 {
767 	int ret_val;
768 
769 	ret_val = genl_register_family(&netlbl_cipsov4_gnl_family);
770 	if (ret_val != 0)
771 		return ret_val;
772 
773 	ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
774 				    &netlbl_cipsov4_genl_c_add);
775 	if (ret_val != 0)
776 		return ret_val;
777 	ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
778 				    &netlbl_cipsov4_genl_c_remove);
779 	if (ret_val != 0)
780 		return ret_val;
781 	ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
782 				    &netlbl_cipsov4_genl_c_list);
783 	if (ret_val != 0)
784 		return ret_val;
785 	ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
786 				    &netlbl_cipsov4_genl_c_listall);
787 	if (ret_val != 0)
788 		return ret_val;
789 
790 	return 0;
791 }
792