xref: /openbmc/linux/net/netlabel/netlabel_mgmt.c (revision 95d4e6be25a68cd9fbe8c0d356b585504d8db1c7)
1 /*
2  * NetLabel Management Support
3  *
4  * This file defines the management 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 <net/sock.h>
36 #include <net/netlink.h>
37 #include <net/genetlink.h>
38 #include <net/netlabel.h>
39 #include <net/cipso_ipv4.h>
40 
41 #include "netlabel_domainhash.h"
42 #include "netlabel_user.h"
43 #include "netlabel_mgmt.h"
44 
45 /* Argument struct for netlbl_domhsh_walk() */
46 struct netlbl_domhsh_walk_arg {
47 	struct netlink_callback *nl_cb;
48 	struct sk_buff *skb;
49 	u32 seq;
50 };
51 
52 /* NetLabel Generic NETLINK CIPSOv4 family */
53 static struct genl_family netlbl_mgmt_gnl_family = {
54 	.id = GENL_ID_GENERATE,
55 	.hdrsize = 0,
56 	.name = NETLBL_NLTYPE_MGMT_NAME,
57 	.version = NETLBL_PROTO_VERSION,
58 	.maxattr = NLBL_MGMT_A_MAX,
59 };
60 
61 /* NetLabel Netlink attribute policy */
62 static struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
63 	[NLBL_MGMT_A_DOMAIN] = { .type = NLA_NUL_STRING },
64 	[NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 },
65 	[NLBL_MGMT_A_VERSION] = { .type = NLA_U32 },
66 	[NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 },
67 };
68 
69 /*
70  * NetLabel Command Handlers
71  */
72 
73 /**
74  * netlbl_mgmt_add - Handle an ADD message
75  * @skb: the NETLINK buffer
76  * @info: the Generic NETLINK info block
77  *
78  * Description:
79  * Process a user generated ADD message and add the domains from the message
80  * to the hash table.  See netlabel.h for a description of the message format.
81  * Returns zero on success, negative values on failure.
82  *
83  */
84 static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
85 {
86 	int ret_val = -EINVAL;
87 	struct netlbl_dom_map *entry = NULL;
88 	size_t tmp_size;
89 	u32 tmp_val;
90 	struct netlbl_audit audit_info;
91 
92 	if (!info->attrs[NLBL_MGMT_A_DOMAIN] ||
93 	    !info->attrs[NLBL_MGMT_A_PROTOCOL])
94 		goto add_failure;
95 
96 	netlbl_netlink_auditinfo(skb, &audit_info);
97 
98 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
99 	if (entry == NULL) {
100 		ret_val = -ENOMEM;
101 		goto add_failure;
102 	}
103 	tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
104 	entry->domain = kmalloc(tmp_size, GFP_KERNEL);
105 	if (entry->domain == NULL) {
106 		ret_val = -ENOMEM;
107 		goto add_failure;
108 	}
109 	entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
110 	nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
111 
112 	switch (entry->type) {
113 	case NETLBL_NLTYPE_UNLABELED:
114 		ret_val = netlbl_domhsh_add(entry, &audit_info);
115 		break;
116 	case NETLBL_NLTYPE_CIPSOV4:
117 		if (!info->attrs[NLBL_MGMT_A_CV4DOI])
118 			goto add_failure;
119 
120 		tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
121 		/* We should be holding a rcu_read_lock() here while we hold
122 		 * the result but since the entry will always be deleted when
123 		 * the CIPSO DOI is deleted we aren't going to keep the
124 		 * lock. */
125 		rcu_read_lock();
126 		entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
127 		if (entry->type_def.cipsov4 == NULL) {
128 			rcu_read_unlock();
129 			goto add_failure;
130 		}
131 		ret_val = netlbl_domhsh_add(entry, &audit_info);
132 		rcu_read_unlock();
133 		break;
134 	default:
135 		goto add_failure;
136 	}
137 	if (ret_val != 0)
138 		goto add_failure;
139 
140 	return 0;
141 
142 add_failure:
143 	if (entry)
144 		kfree(entry->domain);
145 	kfree(entry);
146 	return ret_val;
147 }
148 
149 /**
150  * netlbl_mgmt_remove - Handle a REMOVE message
151  * @skb: the NETLINK buffer
152  * @info: the Generic NETLINK info block
153  *
154  * Description:
155  * Process a user generated REMOVE message and remove the specified domain
156  * mappings.  Returns zero on success, negative values on failure.
157  *
158  */
159 static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info)
160 {
161 	char *domain;
162 	struct netlbl_audit audit_info;
163 
164 	if (!info->attrs[NLBL_MGMT_A_DOMAIN])
165 		return -EINVAL;
166 
167 	netlbl_netlink_auditinfo(skb, &audit_info);
168 
169 	domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]);
170 	return netlbl_domhsh_remove(domain, &audit_info);
171 }
172 
173 /**
174  * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL
175  * @entry: the domain mapping hash table entry
176  * @arg: the netlbl_domhsh_walk_arg structure
177  *
178  * Description:
179  * This function is designed to be used as a callback to the
180  * netlbl_domhsh_walk() function for use in generating a response for a LISTALL
181  * message.  Returns the size of the message on success, negative values on
182  * failure.
183  *
184  */
185 static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg)
186 {
187 	int ret_val = -ENOMEM;
188 	struct netlbl_domhsh_walk_arg *cb_arg = arg;
189 	void *data;
190 
191 	data = netlbl_netlink_hdr_put(cb_arg->skb,
192 				      NETLINK_CB(cb_arg->nl_cb->skb).pid,
193 				      cb_arg->seq,
194 				      netlbl_mgmt_gnl_family.id,
195 				      NLM_F_MULTI,
196 				      NLBL_MGMT_C_LISTALL);
197 	if (data == NULL)
198 		goto listall_cb_failure;
199 
200 	ret_val = nla_put_string(cb_arg->skb,
201 				 NLBL_MGMT_A_DOMAIN,
202 				 entry->domain);
203 	if (ret_val != 0)
204 		goto listall_cb_failure;
205 	ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type);
206 	if (ret_val != 0)
207 		goto listall_cb_failure;
208 	switch (entry->type) {
209 	case NETLBL_NLTYPE_CIPSOV4:
210 		ret_val = nla_put_u32(cb_arg->skb,
211 				      NLBL_MGMT_A_CV4DOI,
212 				      entry->type_def.cipsov4->doi);
213 		if (ret_val != 0)
214 			goto listall_cb_failure;
215 		break;
216 	}
217 
218 	cb_arg->seq++;
219 	return genlmsg_end(cb_arg->skb, data);
220 
221 listall_cb_failure:
222 	genlmsg_cancel(cb_arg->skb, data);
223 	return ret_val;
224 }
225 
226 /**
227  * netlbl_mgmt_listall - Handle a LISTALL message
228  * @skb: the NETLINK buffer
229  * @cb: the NETLINK callback
230  *
231  * Description:
232  * Process a user generated LISTALL message and dumps the domain hash table in
233  * a form suitable for use in a kernel generated LISTALL message.  Returns zero
234  * on success, negative values on failure.
235  *
236  */
237 static int netlbl_mgmt_listall(struct sk_buff *skb,
238 			       struct netlink_callback *cb)
239 {
240 	struct netlbl_domhsh_walk_arg cb_arg;
241 	u32 skip_bkt = cb->args[0];
242 	u32 skip_chain = cb->args[1];
243 
244 	cb_arg.nl_cb = cb;
245 	cb_arg.skb = skb;
246 	cb_arg.seq = cb->nlh->nlmsg_seq;
247 
248 	netlbl_domhsh_walk(&skip_bkt,
249 			   &skip_chain,
250 			   netlbl_mgmt_listall_cb,
251 			   &cb_arg);
252 
253 	cb->args[0] = skip_bkt;
254 	cb->args[1] = skip_chain;
255 	return skb->len;
256 }
257 
258 /**
259  * netlbl_mgmt_adddef - Handle an ADDDEF message
260  * @skb: the NETLINK buffer
261  * @info: the Generic NETLINK info block
262  *
263  * Description:
264  * Process a user generated ADDDEF message and respond accordingly.  Returns
265  * zero on success, negative values on failure.
266  *
267  */
268 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
269 {
270 	int ret_val = -EINVAL;
271 	struct netlbl_dom_map *entry = NULL;
272 	u32 tmp_val;
273 	struct netlbl_audit audit_info;
274 
275 	if (!info->attrs[NLBL_MGMT_A_PROTOCOL])
276 		goto adddef_failure;
277 
278 	netlbl_netlink_auditinfo(skb, &audit_info);
279 
280 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
281 	if (entry == NULL) {
282 		ret_val = -ENOMEM;
283 		goto adddef_failure;
284 	}
285 	entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
286 
287 	switch (entry->type) {
288 	case NETLBL_NLTYPE_UNLABELED:
289 		ret_val = netlbl_domhsh_add_default(entry, &audit_info);
290 		break;
291 	case NETLBL_NLTYPE_CIPSOV4:
292 		if (!info->attrs[NLBL_MGMT_A_CV4DOI])
293 			goto adddef_failure;
294 
295 		tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
296 		/* We should be holding a rcu_read_lock() here while we hold
297 		 * the result but since the entry will always be deleted when
298 		 * the CIPSO DOI is deleted we aren't going to keep the
299 		 * lock. */
300 		rcu_read_lock();
301 		entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
302 		if (entry->type_def.cipsov4 == NULL) {
303 			rcu_read_unlock();
304 			goto adddef_failure;
305 		}
306 		ret_val = netlbl_domhsh_add_default(entry, &audit_info);
307 		rcu_read_unlock();
308 		break;
309 	default:
310 		goto adddef_failure;
311 	}
312 	if (ret_val != 0)
313 		goto adddef_failure;
314 
315 	return 0;
316 
317 adddef_failure:
318 	kfree(entry);
319 	return ret_val;
320 }
321 
322 /**
323  * netlbl_mgmt_removedef - Handle a REMOVEDEF message
324  * @skb: the NETLINK buffer
325  * @info: the Generic NETLINK info block
326  *
327  * Description:
328  * Process a user generated REMOVEDEF message and remove the default domain
329  * mapping.  Returns zero on success, negative values on failure.
330  *
331  */
332 static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info)
333 {
334 	struct netlbl_audit audit_info;
335 
336 	netlbl_netlink_auditinfo(skb, &audit_info);
337 
338 	return netlbl_domhsh_remove_default(&audit_info);
339 }
340 
341 /**
342  * netlbl_mgmt_listdef - Handle a LISTDEF message
343  * @skb: the NETLINK buffer
344  * @info: the Generic NETLINK info block
345  *
346  * Description:
347  * Process a user generated LISTDEF message and dumps the default domain
348  * mapping in a form suitable for use in a kernel generated LISTDEF message.
349  * Returns zero on success, negative values on failure.
350  *
351  */
352 static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
353 {
354 	int ret_val = -ENOMEM;
355 	struct sk_buff *ans_skb = NULL;
356 	void *data;
357 	struct netlbl_dom_map *entry;
358 
359 	ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
360 	if (ans_skb == NULL)
361 		return -ENOMEM;
362 	data = netlbl_netlink_hdr_put(ans_skb,
363 				      info->snd_pid,
364 				      info->snd_seq,
365 				      netlbl_mgmt_gnl_family.id,
366 				      0,
367 				      NLBL_MGMT_C_LISTDEF);
368 	if (data == NULL)
369 		goto listdef_failure;
370 
371 	rcu_read_lock();
372 	entry = netlbl_domhsh_getentry(NULL);
373 	if (entry == NULL) {
374 		ret_val = -ENOENT;
375 		goto listdef_failure_lock;
376 	}
377 	ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type);
378 	if (ret_val != 0)
379 		goto listdef_failure_lock;
380 	switch (entry->type) {
381 	case NETLBL_NLTYPE_CIPSOV4:
382 		ret_val = nla_put_u32(ans_skb,
383 				      NLBL_MGMT_A_CV4DOI,
384 				      entry->type_def.cipsov4->doi);
385 		if (ret_val != 0)
386 			goto listdef_failure_lock;
387 		break;
388 	}
389 	rcu_read_unlock();
390 
391 	genlmsg_end(ans_skb, data);
392 
393 	ret_val = genlmsg_unicast(ans_skb, info->snd_pid);
394 	if (ret_val != 0)
395 		goto listdef_failure;
396 	return 0;
397 
398 listdef_failure_lock:
399 	rcu_read_unlock();
400 listdef_failure:
401 	kfree_skb(ans_skb);
402 	return ret_val;
403 }
404 
405 /**
406  * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response
407  * @skb: the skb to write to
408  * @seq: the NETLINK sequence number
409  * @cb: the NETLINK callback
410  * @protocol: the NetLabel protocol to use in the message
411  *
412  * Description:
413  * This function is to be used in conjunction with netlbl_mgmt_protocols() to
414  * answer a application's PROTOCOLS message.  Returns the size of the message
415  * on success, negative values on failure.
416  *
417  */
418 static int netlbl_mgmt_protocols_cb(struct sk_buff *skb,
419 				    struct netlink_callback *cb,
420 				    u32 protocol)
421 {
422 	int ret_val = -ENOMEM;
423 	void *data;
424 
425 	data = netlbl_netlink_hdr_put(skb,
426 				      NETLINK_CB(cb->skb).pid,
427 				      cb->nlh->nlmsg_seq,
428 				      netlbl_mgmt_gnl_family.id,
429 				      NLM_F_MULTI,
430 				      NLBL_MGMT_C_PROTOCOLS);
431 	if (data == NULL)
432 		goto protocols_cb_failure;
433 
434 	ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol);
435 	if (ret_val != 0)
436 		goto protocols_cb_failure;
437 
438 	return genlmsg_end(skb, data);
439 
440 protocols_cb_failure:
441 	genlmsg_cancel(skb, data);
442 	return ret_val;
443 }
444 
445 /**
446  * netlbl_mgmt_protocols - Handle a PROTOCOLS message
447  * @skb: the NETLINK buffer
448  * @cb: the NETLINK callback
449  *
450  * Description:
451  * Process a user generated PROTOCOLS message and respond accordingly.
452  *
453  */
454 static int netlbl_mgmt_protocols(struct sk_buff *skb,
455 				 struct netlink_callback *cb)
456 {
457 	u32 protos_sent = cb->args[0];
458 
459 	if (protos_sent == 0) {
460 		if (netlbl_mgmt_protocols_cb(skb,
461 					     cb,
462 					     NETLBL_NLTYPE_UNLABELED) < 0)
463 			goto protocols_return;
464 		protos_sent++;
465 	}
466 	if (protos_sent == 1) {
467 		if (netlbl_mgmt_protocols_cb(skb,
468 					     cb,
469 					     NETLBL_NLTYPE_CIPSOV4) < 0)
470 			goto protocols_return;
471 		protos_sent++;
472 	}
473 
474 protocols_return:
475 	cb->args[0] = protos_sent;
476 	return skb->len;
477 }
478 
479 /**
480  * netlbl_mgmt_version - Handle a VERSION message
481  * @skb: the NETLINK buffer
482  * @info: the Generic NETLINK info block
483  *
484  * Description:
485  * Process a user generated VERSION message and respond accordingly.  Returns
486  * zero on success, negative values on failure.
487  *
488  */
489 static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info)
490 {
491 	int ret_val = -ENOMEM;
492 	struct sk_buff *ans_skb = NULL;
493 	void *data;
494 
495 	ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
496 	if (ans_skb == NULL)
497 		return -ENOMEM;
498 	data = netlbl_netlink_hdr_put(ans_skb,
499 				      info->snd_pid,
500 				      info->snd_seq,
501 				      netlbl_mgmt_gnl_family.id,
502 				      0,
503 				      NLBL_MGMT_C_VERSION);
504 	if (data == NULL)
505 		goto version_failure;
506 
507 	ret_val = nla_put_u32(ans_skb,
508 			      NLBL_MGMT_A_VERSION,
509 			      NETLBL_PROTO_VERSION);
510 	if (ret_val != 0)
511 		goto version_failure;
512 
513 	genlmsg_end(ans_skb, data);
514 
515 	ret_val = genlmsg_unicast(ans_skb, info->snd_pid);
516 	if (ret_val != 0)
517 		goto version_failure;
518 	return 0;
519 
520 version_failure:
521 	kfree_skb(ans_skb);
522 	return ret_val;
523 }
524 
525 
526 /*
527  * NetLabel Generic NETLINK Command Definitions
528  */
529 
530 static struct genl_ops netlbl_mgmt_genl_c_add = {
531 	.cmd = NLBL_MGMT_C_ADD,
532 	.flags = GENL_ADMIN_PERM,
533 	.policy = netlbl_mgmt_genl_policy,
534 	.doit = netlbl_mgmt_add,
535 	.dumpit = NULL,
536 };
537 
538 static struct genl_ops netlbl_mgmt_genl_c_remove = {
539 	.cmd = NLBL_MGMT_C_REMOVE,
540 	.flags = GENL_ADMIN_PERM,
541 	.policy = netlbl_mgmt_genl_policy,
542 	.doit = netlbl_mgmt_remove,
543 	.dumpit = NULL,
544 };
545 
546 static struct genl_ops netlbl_mgmt_genl_c_listall = {
547 	.cmd = NLBL_MGMT_C_LISTALL,
548 	.flags = 0,
549 	.policy = netlbl_mgmt_genl_policy,
550 	.doit = NULL,
551 	.dumpit = netlbl_mgmt_listall,
552 };
553 
554 static struct genl_ops netlbl_mgmt_genl_c_adddef = {
555 	.cmd = NLBL_MGMT_C_ADDDEF,
556 	.flags = GENL_ADMIN_PERM,
557 	.policy = netlbl_mgmt_genl_policy,
558 	.doit = netlbl_mgmt_adddef,
559 	.dumpit = NULL,
560 };
561 
562 static struct genl_ops netlbl_mgmt_genl_c_removedef = {
563 	.cmd = NLBL_MGMT_C_REMOVEDEF,
564 	.flags = GENL_ADMIN_PERM,
565 	.policy = netlbl_mgmt_genl_policy,
566 	.doit = netlbl_mgmt_removedef,
567 	.dumpit = NULL,
568 };
569 
570 static struct genl_ops netlbl_mgmt_genl_c_listdef = {
571 	.cmd = NLBL_MGMT_C_LISTDEF,
572 	.flags = 0,
573 	.policy = netlbl_mgmt_genl_policy,
574 	.doit = netlbl_mgmt_listdef,
575 	.dumpit = NULL,
576 };
577 
578 static struct genl_ops netlbl_mgmt_genl_c_protocols = {
579 	.cmd = NLBL_MGMT_C_PROTOCOLS,
580 	.flags = 0,
581 	.policy = netlbl_mgmt_genl_policy,
582 	.doit = NULL,
583 	.dumpit = netlbl_mgmt_protocols,
584 };
585 
586 static struct genl_ops netlbl_mgmt_genl_c_version = {
587 	.cmd = NLBL_MGMT_C_VERSION,
588 	.flags = 0,
589 	.policy = netlbl_mgmt_genl_policy,
590 	.doit = netlbl_mgmt_version,
591 	.dumpit = NULL,
592 };
593 
594 /*
595  * NetLabel Generic NETLINK Protocol Functions
596  */
597 
598 /**
599  * netlbl_mgmt_genl_init - Register the NetLabel management component
600  *
601  * Description:
602  * Register the NetLabel management component with the Generic NETLINK
603  * mechanism.  Returns zero on success, negative values on failure.
604  *
605  */
606 int netlbl_mgmt_genl_init(void)
607 {
608 	int ret_val;
609 
610 	ret_val = genl_register_family(&netlbl_mgmt_gnl_family);
611 	if (ret_val != 0)
612 		return ret_val;
613 
614 	ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
615 				    &netlbl_mgmt_genl_c_add);
616 	if (ret_val != 0)
617 		return ret_val;
618 	ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
619 				    &netlbl_mgmt_genl_c_remove);
620 	if (ret_val != 0)
621 		return ret_val;
622 	ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
623 				    &netlbl_mgmt_genl_c_listall);
624 	if (ret_val != 0)
625 		return ret_val;
626 	ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
627 				    &netlbl_mgmt_genl_c_adddef);
628 	if (ret_val != 0)
629 		return ret_val;
630 	ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
631 				    &netlbl_mgmt_genl_c_removedef);
632 	if (ret_val != 0)
633 		return ret_val;
634 	ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
635 				    &netlbl_mgmt_genl_c_listdef);
636 	if (ret_val != 0)
637 		return ret_val;
638 	ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
639 				    &netlbl_mgmt_genl_c_protocols);
640 	if (ret_val != 0)
641 		return ret_val;
642 	ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
643 				    &netlbl_mgmt_genl_c_version);
644 	if (ret_val != 0)
645 		return ret_val;
646 
647 	return 0;
648 }
649