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