aboutsummaryrefslogtreecommitdiffstats
path: root/net/netlabel/netlabel_mgmt.c
diff options
context:
space:
mode:
authorPaul Moore <paul.moore@hp.com>2006-08-03 19:48:37 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-09-22 17:53:34 -0400
commitd15c345fe3b8dfda0fa5a1d2143a35fffa746a43 (patch)
tree7de6afd5f4de2fca01eaca879e342ab493dc0bba /net/netlabel/netlabel_mgmt.c
parent446fda4f26822b2d42ab3396aafcedf38a9ff2b6 (diff)
[NetLabel]: core NetLabel subsystem
Add a new kernel subsystem, NetLabel, to provide explicit packet labeling services (CIPSO, RIPSO, etc.) to LSM developers. NetLabel is designed to work in conjunction with a LSM to intercept and decode security labels on incoming network packets as well as ensure that outgoing network packets are labeled according to the security mechanism employed by the LSM. The NetLabel subsystem is configured through a Generic NETLINK interface described in the header files included in this patch. Signed-off-by: Paul Moore <paul.moore@hp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlabel/netlabel_mgmt.c')
-rw-r--r--net/netlabel/netlabel_mgmt.c624
1 files changed, 624 insertions, 0 deletions
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
new file mode 100644
index 000000000000..85bc11a1fc46
--- /dev/null
+++ b/net/netlabel/netlabel_mgmt.c
@@ -0,0 +1,624 @@
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/* NetLabel Generic NETLINK CIPSOv4 family */
46static struct genl_family netlbl_mgmt_gnl_family = {
47 .id = GENL_ID_GENERATE,
48 .hdrsize = 0,
49 .name = NETLBL_NLTYPE_MGMT_NAME,
50 .version = NETLBL_PROTO_VERSION,
51 .maxattr = 0,
52};
53
54
55/*
56 * NetLabel Command Handlers
57 */
58
59/**
60 * netlbl_mgmt_add - Handle an ADD message
61 * @skb: the NETLINK buffer
62 * @info: the Generic NETLINK info block
63 *
64 * Description:
65 * Process a user generated ADD message and add the domains from the message
66 * to the hash table. See netlabel.h for a description of the message format.
67 * Returns zero on success, negative values on failure.
68 *
69 */
70static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
71{
72 int ret_val = -EINVAL;
73 struct nlattr *msg_ptr = netlbl_netlink_payload_data(skb);
74 int msg_len = netlbl_netlink_payload_len(skb);
75 u32 count;
76 struct netlbl_dom_map *entry = NULL;
77 u32 iter;
78 u32 tmp_val;
79 int tmp_size;
80
81 ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN);
82 if (ret_val != 0)
83 goto add_failure;
84
85 if (msg_len < NETLBL_LEN_U32)
86 goto add_failure;
87 count = netlbl_getinc_u32(&msg_ptr, &msg_len);
88
89 for (iter = 0; iter < count && msg_len > 0; iter++, entry = NULL) {
90 if (msg_len <= 0) {
91 ret_val = -EINVAL;
92 goto add_failure;
93 }
94 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
95 if (entry == NULL) {
96 ret_val = -ENOMEM;
97 goto add_failure;
98 }
99 tmp_size = nla_len(msg_ptr);
100 if (tmp_size <= 0 || tmp_size > msg_len) {
101 ret_val = -EINVAL;
102 goto add_failure;
103 }
104 entry->domain = kmalloc(tmp_size, GFP_KERNEL);
105 if (entry->domain == NULL) {
106 ret_val = -ENOMEM;
107 goto add_failure;
108 }
109 nla_strlcpy(entry->domain, msg_ptr, tmp_size);
110 entry->domain[tmp_size - 1] = '\0';
111 msg_ptr = nla_next(msg_ptr, &msg_len);
112
113 if (msg_len < NETLBL_LEN_U32) {
114 ret_val = -EINVAL;
115 goto add_failure;
116 }
117 tmp_val = netlbl_getinc_u32(&msg_ptr, &msg_len);
118 entry->type = tmp_val;
119 switch (tmp_val) {
120 case NETLBL_NLTYPE_UNLABELED:
121 ret_val = netlbl_domhsh_add(entry);
122 break;
123 case NETLBL_NLTYPE_CIPSOV4:
124 if (msg_len < NETLBL_LEN_U32) {
125 ret_val = -EINVAL;
126 goto add_failure;
127 }
128 tmp_val = netlbl_getinc_u32(&msg_ptr, &msg_len);
129 /* We should be holding a rcu_read_lock() here
130 * while we hold the result but since the entry
131 * will always be deleted when the CIPSO DOI
132 * is deleted we aren't going to keep the lock. */
133 rcu_read_lock();
134 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
135 if (entry->type_def.cipsov4 == NULL) {
136 rcu_read_unlock();
137 ret_val = -EINVAL;
138 goto add_failure;
139 }
140 ret_val = netlbl_domhsh_add(entry);
141 rcu_read_unlock();
142 break;
143 default:
144 ret_val = -EINVAL;
145 }
146 if (ret_val != 0)
147 goto add_failure;
148 }
149
150 netlbl_netlink_send_ack(info,
151 netlbl_mgmt_gnl_family.id,
152 NLBL_MGMT_C_ACK,
153 NETLBL_E_OK);
154 return 0;
155
156add_failure:
157 if (entry)
158 kfree(entry->domain);
159 kfree(entry);
160 netlbl_netlink_send_ack(info,
161 netlbl_mgmt_gnl_family.id,
162 NLBL_MGMT_C_ACK,
163 -ret_val);
164 return ret_val;
165}
166
167/**
168 * netlbl_mgmt_remove - Handle a REMOVE message
169 * @skb: the NETLINK buffer
170 * @info: the Generic NETLINK info block
171 *
172 * Description:
173 * Process a user generated REMOVE message and remove the specified domain
174 * mappings. Returns zero on success, negative values on failure.
175 *
176 */
177static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info)
178{
179 int ret_val = -EINVAL;
180 struct nlattr *msg_ptr = netlbl_netlink_payload_data(skb);
181 int msg_len = netlbl_netlink_payload_len(skb);
182 u32 count;
183 u32 iter;
184 int tmp_size;
185 unsigned char *domain;
186
187 ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN);
188 if (ret_val != 0)
189 goto remove_return;
190
191 if (msg_len < NETLBL_LEN_U32)
192 goto remove_return;
193 count = netlbl_getinc_u32(&msg_ptr, &msg_len);
194
195 for (iter = 0; iter < count && msg_len > 0; iter++) {
196 if (msg_len <= 0) {
197 ret_val = -EINVAL;
198 goto remove_return;
199 }
200 tmp_size = nla_len(msg_ptr);
201 domain = nla_data(msg_ptr);
202 if (tmp_size <= 0 || tmp_size > msg_len ||
203 domain[tmp_size - 1] != '\0') {
204 ret_val = -EINVAL;
205 goto remove_return;
206 }
207 ret_val = netlbl_domhsh_remove(domain);
208 if (ret_val != 0)
209 goto remove_return;
210 msg_ptr = nla_next(msg_ptr, &msg_len);
211 }
212
213 ret_val = 0;
214
215remove_return:
216 netlbl_netlink_send_ack(info,
217 netlbl_mgmt_gnl_family.id,
218 NLBL_MGMT_C_ACK,
219 -ret_val);
220 return ret_val;
221}
222
223/**
224 * netlbl_mgmt_list - Handle a LIST message
225 * @skb: the NETLINK buffer
226 * @info: the Generic NETLINK info block
227 *
228 * Description:
229 * Process a user generated LIST message and dumps the domain hash table in a
230 * form suitable for use in a kernel generated LIST message. Returns zero on
231 * success, negative values on failure.
232 *
233 */
234static int netlbl_mgmt_list(struct sk_buff *skb, struct genl_info *info)
235{
236 int ret_val = -ENOMEM;
237 struct sk_buff *ans_skb;
238
239 ans_skb = netlbl_domhsh_dump(NLMSG_SPACE(GENL_HDRLEN));
240 if (ans_skb == NULL)
241 goto list_failure;
242 netlbl_netlink_hdr_push(ans_skb,
243 info->snd_pid,
244 0,
245 netlbl_mgmt_gnl_family.id,
246 NLBL_MGMT_C_LIST);
247
248 ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid);
249 if (ret_val != 0)
250 goto list_failure;
251
252 return 0;
253
254list_failure:
255 netlbl_netlink_send_ack(info,
256 netlbl_mgmt_gnl_family.id,
257 NLBL_MGMT_C_ACK,
258 -ret_val);
259 return ret_val;
260}
261
262/**
263 * netlbl_mgmt_adddef - Handle an ADDDEF message
264 * @skb: the NETLINK buffer
265 * @info: the Generic NETLINK info block
266 *
267 * Description:
268 * Process a user generated ADDDEF message and respond accordingly. Returns
269 * zero on success, negative values on failure.
270 *
271 */
272static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
273{
274 int ret_val = -EINVAL;
275 struct nlattr *msg_ptr = netlbl_netlink_payload_data(skb);
276 int msg_len = netlbl_netlink_payload_len(skb);
277 struct netlbl_dom_map *entry = NULL;
278 u32 tmp_val;
279
280 ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN);
281 if (ret_val != 0)
282 goto adddef_failure;
283
284 if (msg_len < NETLBL_LEN_U32)
285 goto adddef_failure;
286 tmp_val = netlbl_getinc_u32(&msg_ptr, &msg_len);
287
288 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
289 if (entry == NULL) {
290 ret_val = -ENOMEM;
291 goto adddef_failure;
292 }
293
294 entry->type = tmp_val;
295 switch (entry->type) {
296 case NETLBL_NLTYPE_UNLABELED:
297 ret_val = netlbl_domhsh_add_default(entry);
298 break;
299 case NETLBL_NLTYPE_CIPSOV4:
300 if (msg_len < NETLBL_LEN_U32) {
301 ret_val = -EINVAL;
302 goto adddef_failure;
303 }
304 tmp_val = netlbl_getinc_u32(&msg_ptr, &msg_len);
305 /* We should be holding a rcu_read_lock here while we
306 * hold the result but since the entry will always be
307 * deleted when the CIPSO DOI is deleted we are going
308 * to skip the lock. */
309 rcu_read_lock();
310 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
311 if (entry->type_def.cipsov4 == NULL) {
312 rcu_read_unlock();
313 ret_val = -EINVAL;
314 goto adddef_failure;
315 }
316 ret_val = netlbl_domhsh_add_default(entry);
317 rcu_read_unlock();
318 break;
319 default:
320 ret_val = -EINVAL;
321 }
322 if (ret_val != 0)
323 goto adddef_failure;
324
325 netlbl_netlink_send_ack(info,
326 netlbl_mgmt_gnl_family.id,
327 NLBL_MGMT_C_ACK,
328 NETLBL_E_OK);
329 return 0;
330
331adddef_failure:
332 kfree(entry);
333 netlbl_netlink_send_ack(info,
334 netlbl_mgmt_gnl_family.id,
335 NLBL_MGMT_C_ACK,
336 -ret_val);
337 return ret_val;
338}
339
340/**
341 * netlbl_mgmt_removedef - Handle a REMOVEDEF message
342 * @skb: the NETLINK buffer
343 * @info: the Generic NETLINK info block
344 *
345 * Description:
346 * Process a user generated REMOVEDEF message and remove the default domain
347 * mapping. Returns zero on success, negative values on failure.
348 *
349 */
350static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info)
351{
352 int ret_val;
353
354 ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN);
355 if (ret_val != 0)
356 goto removedef_return;
357
358 ret_val = netlbl_domhsh_remove_default();
359
360removedef_return:
361 netlbl_netlink_send_ack(info,
362 netlbl_mgmt_gnl_family.id,
363 NLBL_MGMT_C_ACK,
364 -ret_val);
365 return ret_val;
366}
367
368/**
369 * netlbl_mgmt_listdef - Handle a LISTDEF message
370 * @skb: the NETLINK buffer
371 * @info: the Generic NETLINK info block
372 *
373 * Description:
374 * Process a user generated LISTDEF message and dumps the default domain
375 * mapping in a form suitable for use in a kernel generated LISTDEF message.
376 * Returns zero on success, negative values on failure.
377 *
378 */
379static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
380{
381 int ret_val = -ENOMEM;
382 struct sk_buff *ans_skb;
383
384 ans_skb = netlbl_domhsh_dump_default(NLMSG_SPACE(GENL_HDRLEN));
385 if (ans_skb == NULL)
386 goto listdef_failure;
387 netlbl_netlink_hdr_push(ans_skb,
388 info->snd_pid,
389 0,
390 netlbl_mgmt_gnl_family.id,
391 NLBL_MGMT_C_LISTDEF);
392
393 ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid);
394 if (ret_val != 0)
395 goto listdef_failure;
396
397 return 0;
398
399listdef_failure:
400 netlbl_netlink_send_ack(info,
401 netlbl_mgmt_gnl_family.id,
402 NLBL_MGMT_C_ACK,
403 -ret_val);
404 return ret_val;
405}
406
407/**
408 * netlbl_mgmt_modules - Handle a MODULES message
409 * @skb: the NETLINK buffer
410 * @info: the Generic NETLINK info block
411 *
412 * Description:
413 * Process a user generated MODULES message and respond accordingly.
414 *
415 */
416static int netlbl_mgmt_modules(struct sk_buff *skb, struct genl_info *info)
417{
418 int ret_val = -ENOMEM;
419 size_t data_size;
420 u32 mod_count;
421 struct sk_buff *ans_skb = NULL;
422
423 /* unlabeled + cipsov4 */
424 mod_count = 2;
425
426 data_size = GENL_HDRLEN + NETLBL_LEN_U32 + mod_count * NETLBL_LEN_U32;
427 ans_skb = netlbl_netlink_alloc_skb(0, data_size, GFP_KERNEL);
428 if (ans_skb == NULL)
429 goto modules_failure;
430
431 if (netlbl_netlink_hdr_put(ans_skb,
432 info->snd_pid,
433 0,
434 netlbl_mgmt_gnl_family.id,
435 NLBL_MGMT_C_MODULES) == NULL)
436 goto modules_failure;
437
438 ret_val = nla_put_u32(ans_skb, NLA_U32, mod_count);
439 if (ret_val != 0)
440 goto modules_failure;
441 ret_val = nla_put_u32(ans_skb, NLA_U32, NETLBL_NLTYPE_UNLABELED);
442 if (ret_val != 0)
443 goto modules_failure;
444 ret_val = nla_put_u32(ans_skb, NLA_U32, NETLBL_NLTYPE_CIPSOV4);
445 if (ret_val != 0)
446 goto modules_failure;
447
448 ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid);
449 if (ret_val != 0)
450 goto modules_failure;
451
452 return 0;
453
454modules_failure:
455 kfree_skb(ans_skb);
456 netlbl_netlink_send_ack(info,
457 netlbl_mgmt_gnl_family.id,
458 NLBL_MGMT_C_ACK,
459 -ret_val);
460 return ret_val;
461}
462
463/**
464 * netlbl_mgmt_version - Handle a VERSION message
465 * @skb: the NETLINK buffer
466 * @info: the Generic NETLINK info block
467 *
468 * Description:
469 * Process a user generated VERSION message and respond accordingly. Returns
470 * zero on success, negative values on failure.
471 *
472 */
473static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info)
474{
475 int ret_val = -ENOMEM;
476 struct sk_buff *ans_skb = NULL;
477
478 ans_skb = netlbl_netlink_alloc_skb(0,
479 GENL_HDRLEN + NETLBL_LEN_U32,
480 GFP_KERNEL);
481 if (ans_skb == NULL)
482 goto version_failure;
483 if (netlbl_netlink_hdr_put(ans_skb,
484 info->snd_pid,
485 0,
486 netlbl_mgmt_gnl_family.id,
487 NLBL_MGMT_C_VERSION) == NULL)
488 goto version_failure;
489
490 ret_val = nla_put_u32(ans_skb, NLA_U32, NETLBL_PROTO_VERSION);
491 if (ret_val != 0)
492 goto version_failure;
493
494 ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid);
495 if (ret_val != 0)
496 goto version_failure;
497
498 return 0;
499
500version_failure:
501 kfree_skb(ans_skb);
502 netlbl_netlink_send_ack(info,
503 netlbl_mgmt_gnl_family.id,
504 NLBL_MGMT_C_ACK,
505 -ret_val);
506 return ret_val;
507}
508
509
510/*
511 * NetLabel Generic NETLINK Command Definitions
512 */
513
514static struct genl_ops netlbl_mgmt_genl_c_add = {
515 .cmd = NLBL_MGMT_C_ADD,
516 .flags = 0,
517 .doit = netlbl_mgmt_add,
518 .dumpit = NULL,
519};
520
521static struct genl_ops netlbl_mgmt_genl_c_remove = {
522 .cmd = NLBL_MGMT_C_REMOVE,
523 .flags = 0,
524 .doit = netlbl_mgmt_remove,
525 .dumpit = NULL,
526};
527
528static struct genl_ops netlbl_mgmt_genl_c_list = {
529 .cmd = NLBL_MGMT_C_LIST,
530 .flags = 0,
531 .doit = netlbl_mgmt_list,
532 .dumpit = NULL,
533};
534
535static struct genl_ops netlbl_mgmt_genl_c_adddef = {
536 .cmd = NLBL_MGMT_C_ADDDEF,
537 .flags = 0,
538 .doit = netlbl_mgmt_adddef,
539 .dumpit = NULL,
540};
541
542static struct genl_ops netlbl_mgmt_genl_c_removedef = {
543 .cmd = NLBL_MGMT_C_REMOVEDEF,
544 .flags = 0,
545 .doit = netlbl_mgmt_removedef,
546 .dumpit = NULL,
547};
548
549static struct genl_ops netlbl_mgmt_genl_c_listdef = {
550 .cmd = NLBL_MGMT_C_LISTDEF,
551 .flags = 0,
552 .doit = netlbl_mgmt_listdef,
553 .dumpit = NULL,
554};
555
556static struct genl_ops netlbl_mgmt_genl_c_modules = {
557 .cmd = NLBL_MGMT_C_MODULES,
558 .flags = 0,
559 .doit = netlbl_mgmt_modules,
560 .dumpit = NULL,
561};
562
563static struct genl_ops netlbl_mgmt_genl_c_version = {
564 .cmd = NLBL_MGMT_C_VERSION,
565 .flags = 0,
566 .doit = netlbl_mgmt_version,
567 .dumpit = NULL,
568};
569
570/*
571 * NetLabel Generic NETLINK Protocol Functions
572 */
573
574/**
575 * netlbl_mgmt_genl_init - Register the NetLabel management component
576 *
577 * Description:
578 * Register the NetLabel management component with the Generic NETLINK
579 * mechanism. Returns zero on success, negative values on failure.
580 *
581 */
582int netlbl_mgmt_genl_init(void)
583{
584 int ret_val;
585
586 ret_val = genl_register_family(&netlbl_mgmt_gnl_family);
587 if (ret_val != 0)
588 return ret_val;
589
590 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
591 &netlbl_mgmt_genl_c_add);
592 if (ret_val != 0)
593 return ret_val;
594 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
595 &netlbl_mgmt_genl_c_remove);
596 if (ret_val != 0)
597 return ret_val;
598 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
599 &netlbl_mgmt_genl_c_list);
600 if (ret_val != 0)
601 return ret_val;
602 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
603 &netlbl_mgmt_genl_c_adddef);
604 if (ret_val != 0)
605 return ret_val;
606 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
607 &netlbl_mgmt_genl_c_removedef);
608 if (ret_val != 0)
609 return ret_val;
610 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
611 &netlbl_mgmt_genl_c_listdef);
612 if (ret_val != 0)
613 return ret_val;
614 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
615 &netlbl_mgmt_genl_c_modules);
616 if (ret_val != 0)
617 return ret_val;
618 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
619 &netlbl_mgmt_genl_c_version);
620 if (ret_val != 0)
621 return ret_val;
622
623 return 0;
624}