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, 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 <linux/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 	struct netlbl_audit audit_info;
426 
427 	if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
428 	    !info->attrs[NLBL_CIPSOV4_A_MTYPE])
429 		return -EINVAL;
430 
431 	netlbl_netlink_auditinfo(skb, &audit_info);
432 	switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {
433 	case CIPSO_V4_MAP_TRANS:
434 		ret_val = netlbl_cipsov4_add_std(info, &audit_info);
435 		break;
436 	case CIPSO_V4_MAP_PASS:
437 		ret_val = netlbl_cipsov4_add_pass(info, &audit_info);
438 		break;
439 	case CIPSO_V4_MAP_LOCAL:
440 		ret_val = netlbl_cipsov4_add_local(info, &audit_info);
441 		break;
442 	}
443 	if (ret_val == 0)
444 		atomic_inc(&netlabel_mgmt_protocount);
445 
446 	return ret_val;
447 }
448 
449 /**
450  * netlbl_cipsov4_list - Handle a LIST message
451  * @skb: the NETLINK buffer
452  * @info: the Generic NETLINK info block
453  *
454  * Description:
455  * Process a user generated LIST message and respond accordingly.  While the
456  * response message generated by the kernel is straightforward, determining
457  * before hand the size of the buffer to allocate is not (we have to generate
458  * the message to know the size).  In order to keep this function sane what we
459  * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in
460  * that size, if we fail then we restart with a larger buffer and try again.
461  * We continue in this manner until we hit a limit of failed attempts then we
462  * give up and just send an error message.  Returns zero on success and
463  * negative values on error.
464  *
465  */
466 static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
467 {
468 	int ret_val;
469 	struct sk_buff *ans_skb = NULL;
470 	u32 nlsze_mult = 1;
471 	void *data;
472 	u32 doi;
473 	struct nlattr *nla_a;
474 	struct nlattr *nla_b;
475 	struct cipso_v4_doi *doi_def;
476 	u32 iter;
477 
478 	if (!info->attrs[NLBL_CIPSOV4_A_DOI]) {
479 		ret_val = -EINVAL;
480 		goto list_failure;
481 	}
482 
483 list_start:
484 	ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE * nlsze_mult, GFP_KERNEL);
485 	if (ans_skb == NULL) {
486 		ret_val = -ENOMEM;
487 		goto list_failure;
488 	}
489 	data = genlmsg_put_reply(ans_skb, info, &netlbl_cipsov4_gnl_family,
490 				 0, NLBL_CIPSOV4_C_LIST);
491 	if (data == NULL) {
492 		ret_val = -ENOMEM;
493 		goto list_failure;
494 	}
495 
496 	doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
497 
498 	rcu_read_lock();
499 	doi_def = cipso_v4_doi_getdef(doi);
500 	if (doi_def == NULL) {
501 		ret_val = -EINVAL;
502 		goto list_failure_lock;
503 	}
504 
505 	ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
506 	if (ret_val != 0)
507 		goto list_failure_lock;
508 
509 	nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_TAGLST);
510 	if (nla_a == NULL) {
511 		ret_val = -ENOMEM;
512 		goto list_failure_lock;
513 	}
514 	for (iter = 0;
515 	     iter < CIPSO_V4_TAG_MAXCNT &&
516 	       doi_def->tags[iter] != CIPSO_V4_TAG_INVALID;
517 	     iter++) {
518 		ret_val = nla_put_u8(ans_skb,
519 				     NLBL_CIPSOV4_A_TAG,
520 				     doi_def->tags[iter]);
521 		if (ret_val != 0)
522 			goto list_failure_lock;
523 	}
524 	nla_nest_end(ans_skb, nla_a);
525 
526 	switch (doi_def->type) {
527 	case CIPSO_V4_MAP_TRANS:
528 		nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
529 		if (nla_a == NULL) {
530 			ret_val = -ENOMEM;
531 			goto list_failure_lock;
532 		}
533 		for (iter = 0;
534 		     iter < doi_def->map.std->lvl.local_size;
535 		     iter++) {
536 			if (doi_def->map.std->lvl.local[iter] ==
537 			    CIPSO_V4_INV_LVL)
538 				continue;
539 
540 			nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVL);
541 			if (nla_b == NULL) {
542 				ret_val = -ENOMEM;
543 				goto list_retry;
544 			}
545 			ret_val = nla_put_u32(ans_skb,
546 					      NLBL_CIPSOV4_A_MLSLVLLOC,
547 					      iter);
548 			if (ret_val != 0)
549 				goto list_retry;
550 			ret_val = nla_put_u32(ans_skb,
551 					    NLBL_CIPSOV4_A_MLSLVLREM,
552 					    doi_def->map.std->lvl.local[iter]);
553 			if (ret_val != 0)
554 				goto list_retry;
555 			nla_nest_end(ans_skb, nla_b);
556 		}
557 		nla_nest_end(ans_skb, nla_a);
558 
559 		nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCATLST);
560 		if (nla_a == NULL) {
561 			ret_val = -ENOMEM;
562 			goto list_retry;
563 		}
564 		for (iter = 0;
565 		     iter < doi_def->map.std->cat.local_size;
566 		     iter++) {
567 			if (doi_def->map.std->cat.local[iter] ==
568 			    CIPSO_V4_INV_CAT)
569 				continue;
570 
571 			nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCAT);
572 			if (nla_b == NULL) {
573 				ret_val = -ENOMEM;
574 				goto list_retry;
575 			}
576 			ret_val = nla_put_u32(ans_skb,
577 					      NLBL_CIPSOV4_A_MLSCATLOC,
578 					      iter);
579 			if (ret_val != 0)
580 				goto list_retry;
581 			ret_val = nla_put_u32(ans_skb,
582 					    NLBL_CIPSOV4_A_MLSCATREM,
583 					    doi_def->map.std->cat.local[iter]);
584 			if (ret_val != 0)
585 				goto list_retry;
586 			nla_nest_end(ans_skb, nla_b);
587 		}
588 		nla_nest_end(ans_skb, nla_a);
589 
590 		break;
591 	}
592 	rcu_read_unlock();
593 
594 	genlmsg_end(ans_skb, data);
595 	return genlmsg_reply(ans_skb, info);
596 
597 list_retry:
598 	/* XXX - this limit is a guesstimate */
599 	if (nlsze_mult < 4) {
600 		rcu_read_unlock();
601 		kfree_skb(ans_skb);
602 		nlsze_mult *= 2;
603 		goto list_start;
604 	}
605 list_failure_lock:
606 	rcu_read_unlock();
607 list_failure:
608 	kfree_skb(ans_skb);
609 	return ret_val;
610 }
611 
612 /**
613  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
614  * @doi_def: the CIPSOv4 DOI definition
615  * @arg: the netlbl_cipsov4_doiwalk_arg structure
616  *
617  * Description:
618  * This function is designed to be used as a callback to the
619  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
620  * message.  Returns the size of the message on success, negative values on
621  * failure.
622  *
623  */
624 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
625 {
626 	int ret_val = -ENOMEM;
627 	struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
628 	void *data;
629 
630 	data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
631 			   cb_arg->seq, &netlbl_cipsov4_gnl_family,
632 			   NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
633 	if (data == NULL)
634 		goto listall_cb_failure;
635 
636 	ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
637 	if (ret_val != 0)
638 		goto listall_cb_failure;
639 	ret_val = nla_put_u32(cb_arg->skb,
640 			      NLBL_CIPSOV4_A_MTYPE,
641 			      doi_def->type);
642 	if (ret_val != 0)
643 		goto listall_cb_failure;
644 
645 	return genlmsg_end(cb_arg->skb, data);
646 
647 listall_cb_failure:
648 	genlmsg_cancel(cb_arg->skb, data);
649 	return ret_val;
650 }
651 
652 /**
653  * netlbl_cipsov4_listall - Handle a LISTALL message
654  * @skb: the NETLINK buffer
655  * @cb: the NETLINK callback
656  *
657  * Description:
658  * Process a user generated LISTALL message and respond accordingly.  Returns
659  * zero on success and negative values on error.
660  *
661  */
662 static int netlbl_cipsov4_listall(struct sk_buff *skb,
663 				  struct netlink_callback *cb)
664 {
665 	struct netlbl_cipsov4_doiwalk_arg cb_arg;
666 	u32 doi_skip = cb->args[0];
667 
668 	cb_arg.nl_cb = cb;
669 	cb_arg.skb = skb;
670 	cb_arg.seq = cb->nlh->nlmsg_seq;
671 
672 	cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
673 
674 	cb->args[0] = doi_skip;
675 	return skb->len;
676 }
677 
678 /**
679  * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE
680  * @entry: LSM domain mapping entry
681  * @arg: the netlbl_domhsh_walk_arg structure
682  *
683  * Description:
684  * This function is intended for use by netlbl_cipsov4_remove() as the callback
685  * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
686  * which are associated with the CIPSO DOI specified in @arg.  Returns zero on
687  * success, negative values on failure.
688  *
689  */
690 static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
691 {
692 	struct netlbl_domhsh_walk_arg *cb_arg = arg;
693 
694 	if (entry->type == NETLBL_NLTYPE_CIPSOV4 &&
695 	    entry->type_def.cipsov4->doi == cb_arg->doi)
696 		return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
697 
698 	return 0;
699 }
700 
701 /**
702  * netlbl_cipsov4_remove - Handle a REMOVE message
703  * @skb: the NETLINK buffer
704  * @info: the Generic NETLINK info block
705  *
706  * Description:
707  * Process a user generated REMOVE message and respond accordingly.  Returns
708  * zero on success, negative values on failure.
709  *
710  */
711 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
712 {
713 	int ret_val = -EINVAL;
714 	struct netlbl_domhsh_walk_arg cb_arg;
715 	struct netlbl_audit audit_info;
716 	u32 skip_bkt = 0;
717 	u32 skip_chain = 0;
718 
719 	if (!info->attrs[NLBL_CIPSOV4_A_DOI])
720 		return -EINVAL;
721 
722 	netlbl_netlink_auditinfo(skb, &audit_info);
723 	cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
724 	cb_arg.audit_info = &audit_info;
725 	ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
726 				     netlbl_cipsov4_remove_cb, &cb_arg);
727 	if (ret_val == 0 || ret_val == -ENOENT) {
728 		ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info);
729 		if (ret_val == 0)
730 			atomic_dec(&netlabel_mgmt_protocount);
731 	}
732 
733 	return ret_val;
734 }
735 
736 /*
737  * NetLabel Generic NETLINK Command Definitions
738  */
739 
740 static struct genl_ops netlbl_cipsov4_ops[] = {
741 	{
742 	.cmd = NLBL_CIPSOV4_C_ADD,
743 	.flags = GENL_ADMIN_PERM,
744 	.policy = netlbl_cipsov4_genl_policy,
745 	.doit = netlbl_cipsov4_add,
746 	.dumpit = NULL,
747 	},
748 	{
749 	.cmd = NLBL_CIPSOV4_C_REMOVE,
750 	.flags = GENL_ADMIN_PERM,
751 	.policy = netlbl_cipsov4_genl_policy,
752 	.doit = netlbl_cipsov4_remove,
753 	.dumpit = NULL,
754 	},
755 	{
756 	.cmd = NLBL_CIPSOV4_C_LIST,
757 	.flags = 0,
758 	.policy = netlbl_cipsov4_genl_policy,
759 	.doit = netlbl_cipsov4_list,
760 	.dumpit = NULL,
761 	},
762 	{
763 	.cmd = NLBL_CIPSOV4_C_LISTALL,
764 	.flags = 0,
765 	.policy = netlbl_cipsov4_genl_policy,
766 	.doit = NULL,
767 	.dumpit = netlbl_cipsov4_listall,
768 	},
769 };
770 
771 /*
772  * NetLabel Generic NETLINK Protocol Functions
773  */
774 
775 /**
776  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
777  *
778  * Description:
779  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
780  * mechanism.  Returns zero on success, negative values on failure.
781  *
782  */
783 int __init netlbl_cipsov4_genl_init(void)
784 {
785 	return genl_register_family_with_ops(&netlbl_cipsov4_gnl_family,
786 		netlbl_cipsov4_ops, ARRAY_SIZE(netlbl_cipsov4_ops));
787 }
788