xref: /linux/net/netlabel/netlabel_calipso.c (revision d7cce01504a0ccb95b5007d846560cfccbc1947f)
1 /*
2  * NetLabel CALIPSO/IPv6 Support
3  *
4  * This file defines the CALIPSO/IPv6 functions for the NetLabel system.  The
5  * NetLabel system manages static and dynamic label mappings for network
6  * protocols such as CIPSO and CALIPSO.
7  *
8  * Authors: Paul Moore <paul@paul-moore.com>
9  *          Huw Davies <huw@codeweavers.com>
10  *
11  */
12 
13 /* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
14  * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015
15  *
16  * This program is free software;  you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
24  * the GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program;  if not, see <http://www.gnu.org/licenses/>.
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/calipso.h>
42 #include <linux/atomic.h>
43 
44 #include "netlabel_user.h"
45 #include "netlabel_calipso.h"
46 #include "netlabel_mgmt.h"
47 #include "netlabel_domainhash.h"
48 
49 /* Argument struct for calipso_doi_walk() */
50 struct netlbl_calipso_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 CALIPSO family */
63 static struct genl_family netlbl_calipso_gnl_family = {
64 	.id = GENL_ID_GENERATE,
65 	.hdrsize = 0,
66 	.name = NETLBL_NLTYPE_CALIPSO_NAME,
67 	.version = NETLBL_PROTO_VERSION,
68 	.maxattr = NLBL_CALIPSO_A_MAX,
69 };
70 
71 /* NetLabel Netlink attribute policy */
72 static const struct nla_policy calipso_genl_policy[NLBL_CALIPSO_A_MAX + 1] = {
73 	[NLBL_CALIPSO_A_DOI] = { .type = NLA_U32 },
74 	[NLBL_CALIPSO_A_MTYPE] = { .type = NLA_U32 },
75 };
76 
77 /* NetLabel Command Handlers
78  */
79 /**
80  * netlbl_calipso_add_pass - Adds a CALIPSO pass DOI definition
81  * @info: the Generic NETLINK info block
82  * @audit_info: NetLabel audit information
83  *
84  * Description:
85  * Create a new CALIPSO_MAP_PASS DOI definition based on the given ADD message
86  * and add it to the CALIPSO engine.  Return zero on success and non-zero on
87  * error.
88  *
89  */
90 static int netlbl_calipso_add_pass(struct genl_info *info,
91 				   struct netlbl_audit *audit_info)
92 {
93 	int ret_val;
94 	struct calipso_doi *doi_def = NULL;
95 
96 	doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
97 	if (!doi_def)
98 		return -ENOMEM;
99 	doi_def->type = CALIPSO_MAP_PASS;
100 	doi_def->doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]);
101 	ret_val = calipso_doi_add(doi_def, audit_info);
102 	if (ret_val != 0)
103 		calipso_doi_free(doi_def);
104 
105 	return ret_val;
106 }
107 
108 /**
109  * netlbl_calipso_add - Handle an ADD message
110  * @skb: the NETLINK buffer
111  * @info: the Generic NETLINK info block
112  *
113  * Description:
114  * Create a new DOI definition based on the given ADD message and add it to the
115  * CALIPSO engine.  Returns zero on success, negative values on failure.
116  *
117  */
118 static int netlbl_calipso_add(struct sk_buff *skb, struct genl_info *info)
119 
120 {
121 	int ret_val = -EINVAL;
122 	struct netlbl_audit audit_info;
123 
124 	if (!info->attrs[NLBL_CALIPSO_A_DOI] ||
125 	    !info->attrs[NLBL_CALIPSO_A_MTYPE])
126 		return -EINVAL;
127 
128 	netlbl_netlink_auditinfo(skb, &audit_info);
129 	switch (nla_get_u32(info->attrs[NLBL_CALIPSO_A_MTYPE])) {
130 	case CALIPSO_MAP_PASS:
131 		ret_val = netlbl_calipso_add_pass(info, &audit_info);
132 		break;
133 	}
134 	if (ret_val == 0)
135 		atomic_inc(&netlabel_mgmt_protocount);
136 
137 	return ret_val;
138 }
139 
140 /**
141  * netlbl_calipso_list - Handle a LIST message
142  * @skb: the NETLINK buffer
143  * @info: the Generic NETLINK info block
144  *
145  * Description:
146  * Process a user generated LIST message and respond accordingly.
147  * Returns zero on success and negative values on error.
148  *
149  */
150 static int netlbl_calipso_list(struct sk_buff *skb, struct genl_info *info)
151 {
152 	int ret_val;
153 	struct sk_buff *ans_skb = NULL;
154 	void *data;
155 	u32 doi;
156 	struct calipso_doi *doi_def;
157 
158 	if (!info->attrs[NLBL_CALIPSO_A_DOI]) {
159 		ret_val = -EINVAL;
160 		goto list_failure;
161 	}
162 
163 	doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]);
164 
165 	doi_def = calipso_doi_getdef(doi);
166 	if (!doi_def) {
167 		ret_val = -EINVAL;
168 		goto list_failure;
169 	}
170 
171 	ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
172 	if (!ans_skb) {
173 		ret_val = -ENOMEM;
174 		goto list_failure_put;
175 	}
176 	data = genlmsg_put_reply(ans_skb, info, &netlbl_calipso_gnl_family,
177 				 0, NLBL_CALIPSO_C_LIST);
178 	if (!data) {
179 		ret_val = -ENOMEM;
180 		goto list_failure_put;
181 	}
182 
183 	ret_val = nla_put_u32(ans_skb, NLBL_CALIPSO_A_MTYPE, doi_def->type);
184 	if (ret_val != 0)
185 		goto list_failure_put;
186 
187 	calipso_doi_putdef(doi_def);
188 
189 	genlmsg_end(ans_skb, data);
190 	return genlmsg_reply(ans_skb, info);
191 
192 list_failure_put:
193 	calipso_doi_putdef(doi_def);
194 list_failure:
195 	kfree_skb(ans_skb);
196 	return ret_val;
197 }
198 
199 /**
200  * netlbl_calipso_listall_cb - calipso_doi_walk() callback for LISTALL
201  * @doi_def: the CALIPSO DOI definition
202  * @arg: the netlbl_calipso_doiwalk_arg structure
203  *
204  * Description:
205  * This function is designed to be used as a callback to the
206  * calipso_doi_walk() function for use in generating a response for a LISTALL
207  * message.  Returns the size of the message on success, negative values on
208  * failure.
209  *
210  */
211 static int netlbl_calipso_listall_cb(struct calipso_doi *doi_def, void *arg)
212 {
213 	int ret_val = -ENOMEM;
214 	struct netlbl_calipso_doiwalk_arg *cb_arg = arg;
215 	void *data;
216 
217 	data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
218 			   cb_arg->seq, &netlbl_calipso_gnl_family,
219 			   NLM_F_MULTI, NLBL_CALIPSO_C_LISTALL);
220 	if (!data)
221 		goto listall_cb_failure;
222 
223 	ret_val = nla_put_u32(cb_arg->skb, NLBL_CALIPSO_A_DOI, doi_def->doi);
224 	if (ret_val != 0)
225 		goto listall_cb_failure;
226 	ret_val = nla_put_u32(cb_arg->skb,
227 			      NLBL_CALIPSO_A_MTYPE,
228 			      doi_def->type);
229 	if (ret_val != 0)
230 		goto listall_cb_failure;
231 
232 	genlmsg_end(cb_arg->skb, data);
233 	return 0;
234 
235 listall_cb_failure:
236 	genlmsg_cancel(cb_arg->skb, data);
237 	return ret_val;
238 }
239 
240 /**
241  * netlbl_calipso_listall - Handle a LISTALL message
242  * @skb: the NETLINK buffer
243  * @cb: the NETLINK callback
244  *
245  * Description:
246  * Process a user generated LISTALL message and respond accordingly.  Returns
247  * zero on success and negative values on error.
248  *
249  */
250 static int netlbl_calipso_listall(struct sk_buff *skb,
251 				  struct netlink_callback *cb)
252 {
253 	struct netlbl_calipso_doiwalk_arg cb_arg;
254 	u32 doi_skip = cb->args[0];
255 
256 	cb_arg.nl_cb = cb;
257 	cb_arg.skb = skb;
258 	cb_arg.seq = cb->nlh->nlmsg_seq;
259 
260 	calipso_doi_walk(&doi_skip, netlbl_calipso_listall_cb, &cb_arg);
261 
262 	cb->args[0] = doi_skip;
263 	return skb->len;
264 }
265 
266 /**
267  * netlbl_calipso_remove_cb - netlbl_calipso_remove() callback for REMOVE
268  * @entry: LSM domain mapping entry
269  * @arg: the netlbl_domhsh_walk_arg structure
270  *
271  * Description:
272  * This function is intended for use by netlbl_calipso_remove() as the callback
273  * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
274  * which are associated with the CALIPSO DOI specified in @arg.  Returns zero on
275  * success, negative values on failure.
276  *
277  */
278 static int netlbl_calipso_remove_cb(struct netlbl_dom_map *entry, void *arg)
279 {
280 	struct netlbl_domhsh_walk_arg *cb_arg = arg;
281 
282 	if (entry->def.type == NETLBL_NLTYPE_CALIPSO &&
283 	    entry->def.calipso->doi == cb_arg->doi)
284 		return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
285 
286 	return 0;
287 }
288 
289 /**
290  * netlbl_calipso_remove - Handle a REMOVE message
291  * @skb: the NETLINK buffer
292  * @info: the Generic NETLINK info block
293  *
294  * Description:
295  * Process a user generated REMOVE message and respond accordingly.  Returns
296  * zero on success, negative values on failure.
297  *
298  */
299 static int netlbl_calipso_remove(struct sk_buff *skb, struct genl_info *info)
300 {
301 	int ret_val = -EINVAL;
302 	struct netlbl_domhsh_walk_arg cb_arg;
303 	struct netlbl_audit audit_info;
304 	u32 skip_bkt = 0;
305 	u32 skip_chain = 0;
306 
307 	if (!info->attrs[NLBL_CALIPSO_A_DOI])
308 		return -EINVAL;
309 
310 	netlbl_netlink_auditinfo(skb, &audit_info);
311 	cb_arg.doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]);
312 	cb_arg.audit_info = &audit_info;
313 	ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
314 				     netlbl_calipso_remove_cb, &cb_arg);
315 	if (ret_val == 0 || ret_val == -ENOENT) {
316 		ret_val = calipso_doi_remove(cb_arg.doi, &audit_info);
317 		if (ret_val == 0)
318 			atomic_dec(&netlabel_mgmt_protocount);
319 	}
320 
321 	return ret_val;
322 }
323 
324 /* NetLabel Generic NETLINK Command Definitions
325  */
326 
327 static const struct genl_ops netlbl_calipso_ops[] = {
328 	{
329 	.cmd = NLBL_CALIPSO_C_ADD,
330 	.flags = GENL_ADMIN_PERM,
331 	.policy = calipso_genl_policy,
332 	.doit = netlbl_calipso_add,
333 	.dumpit = NULL,
334 	},
335 	{
336 	.cmd = NLBL_CALIPSO_C_REMOVE,
337 	.flags = GENL_ADMIN_PERM,
338 	.policy = calipso_genl_policy,
339 	.doit = netlbl_calipso_remove,
340 	.dumpit = NULL,
341 	},
342 	{
343 	.cmd = NLBL_CALIPSO_C_LIST,
344 	.flags = 0,
345 	.policy = calipso_genl_policy,
346 	.doit = netlbl_calipso_list,
347 	.dumpit = NULL,
348 	},
349 	{
350 	.cmd = NLBL_CALIPSO_C_LISTALL,
351 	.flags = 0,
352 	.policy = calipso_genl_policy,
353 	.doit = NULL,
354 	.dumpit = netlbl_calipso_listall,
355 	},
356 };
357 
358 /* NetLabel Generic NETLINK Protocol Functions
359  */
360 
361 /**
362  * netlbl_calipso_genl_init - Register the CALIPSO NetLabel component
363  *
364  * Description:
365  * Register the CALIPSO packet NetLabel component with the Generic NETLINK
366  * mechanism.  Returns zero on success, negative values on failure.
367  *
368  */
369 int __init netlbl_calipso_genl_init(void)
370 {
371 	return genl_register_family_with_ops(&netlbl_calipso_gnl_family,
372 					     netlbl_calipso_ops);
373 }
374 
375 static const struct netlbl_calipso_ops *calipso_ops;
376 
377 /**
378  * netlbl_calipso_ops_register - Register the CALIPSO operations
379  *
380  * Description:
381  * Register the CALIPSO packet engine operations.
382  *
383  */
384 const struct netlbl_calipso_ops *
385 netlbl_calipso_ops_register(const struct netlbl_calipso_ops *ops)
386 {
387 	return xchg(&calipso_ops, ops);
388 }
389 EXPORT_SYMBOL(netlbl_calipso_ops_register);
390 
391 static const struct netlbl_calipso_ops *netlbl_calipso_ops_get(void)
392 {
393 	return ACCESS_ONCE(calipso_ops);
394 }
395 
396 /**
397  * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine
398  * @doi_def: the DOI structure
399  * @audit_info: NetLabel audit information
400  *
401  * Description:
402  * The caller defines a new DOI for use by the CALIPSO engine and calls this
403  * function to add it to the list of acceptable domains.  The caller must
404  * ensure that the mapping table specified in @doi_def->map meets all of the
405  * requirements of the mapping type (see calipso.h for details).  Returns
406  * zero on success and non-zero on failure.
407  *
408  */
409 int calipso_doi_add(struct calipso_doi *doi_def,
410 		    struct netlbl_audit *audit_info)
411 {
412 	int ret_val = -ENOMSG;
413 	const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
414 
415 	if (ops)
416 		ret_val = ops->doi_add(doi_def, audit_info);
417 	return ret_val;
418 }
419 
420 /**
421  * calipso_doi_free - Frees a DOI definition
422  * @doi_def: the DOI definition
423  *
424  * Description:
425  * This function frees all of the memory associated with a DOI definition.
426  *
427  */
428 void calipso_doi_free(struct calipso_doi *doi_def)
429 {
430 	const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
431 
432 	if (ops)
433 		ops->doi_free(doi_def);
434 }
435 
436 /**
437  * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine
438  * @doi: the DOI value
439  * @audit_secid: the LSM secid to use in the audit message
440  *
441  * Description:
442  * Removes a DOI definition from the CALIPSO engine.  The NetLabel routines will
443  * be called to release their own LSM domain mappings as well as our own
444  * domain list.  Returns zero on success and negative values on failure.
445  *
446  */
447 int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info)
448 {
449 	int ret_val = -ENOMSG;
450 	const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
451 
452 	if (ops)
453 		ret_val = ops->doi_remove(doi, audit_info);
454 	return ret_val;
455 }
456 
457 /**
458  * calipso_doi_getdef - Returns a reference to a valid DOI definition
459  * @doi: the DOI value
460  *
461  * Description:
462  * Searches for a valid DOI definition and if one is found it is returned to
463  * the caller.  Otherwise NULL is returned.  The caller must ensure that
464  * calipso_doi_putdef() is called when the caller is done.
465  *
466  */
467 struct calipso_doi *calipso_doi_getdef(u32 doi)
468 {
469 	struct calipso_doi *ret_val = NULL;
470 	const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
471 
472 	if (ops)
473 		ret_val = ops->doi_getdef(doi);
474 	return ret_val;
475 }
476 
477 /**
478  * calipso_doi_putdef - Releases a reference for the given DOI definition
479  * @doi_def: the DOI definition
480  *
481  * Description:
482  * Releases a DOI definition reference obtained from calipso_doi_getdef().
483  *
484  */
485 void calipso_doi_putdef(struct calipso_doi *doi_def)
486 {
487 	const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
488 
489 	if (ops)
490 		ops->doi_putdef(doi_def);
491 }
492 
493 /**
494  * calipso_doi_walk - Iterate through the DOI definitions
495  * @skip_cnt: skip past this number of DOI definitions, updated
496  * @callback: callback for each DOI definition
497  * @cb_arg: argument for the callback function
498  *
499  * Description:
500  * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
501  * For each entry call @callback, if @callback returns a negative value stop
502  * 'walking' through the list and return.  Updates the value in @skip_cnt upon
503  * return.  Returns zero on success, negative values on failure.
504  *
505  */
506 int calipso_doi_walk(u32 *skip_cnt,
507 		     int (*callback)(struct calipso_doi *doi_def, void *arg),
508 		     void *cb_arg)
509 {
510 	int ret_val = -ENOMSG;
511 	const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
512 
513 	if (ops)
514 		ret_val = ops->doi_walk(skip_cnt, callback, cb_arg);
515 	return ret_val;
516 }
517