Linux Audio

Check our new training course

Linux kernel drivers training

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