[NetLabel]: rework the Netlink attribute handling (part 1)
[linux-block.git] / net / netlabel / netlabel_mgmt.c
CommitLineData
d15c345f
PM
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}