]> bbs.cooldavid.org Git - net-next-2.6.git/blob - net/netlabel/netlabel_cipso_v4.c
[NETLINK]: Do precise netlink message allocations where possible
[net-next-2.6.git] / net / netlabel / netlabel_cipso_v4.c
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.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 <linux/audit.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
42 #include "netlabel_user.h"
43 #include "netlabel_cipso_v4.h"
44
45 /* Argument struct for cipso_v4_doi_walk() */
46 struct netlbl_cipsov4_doiwalk_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_cipsov4_gnl_family = {
54         .id = GENL_ID_GENERATE,
55         .hdrsize = 0,
56         .name = NETLBL_NLTYPE_CIPSOV4_NAME,
57         .version = NETLBL_PROTO_VERSION,
58         .maxattr = NLBL_CIPSOV4_A_MAX,
59 };
60
61 /* NetLabel Netlink attribute policy */
62 static struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1] = {
63         [NLBL_CIPSOV4_A_DOI] = { .type = NLA_U32 },
64         [NLBL_CIPSOV4_A_MTYPE] = { .type = NLA_U32 },
65         [NLBL_CIPSOV4_A_TAG] = { .type = NLA_U8 },
66         [NLBL_CIPSOV4_A_TAGLST] = { .type = NLA_NESTED },
67         [NLBL_CIPSOV4_A_MLSLVLLOC] = { .type = NLA_U32 },
68         [NLBL_CIPSOV4_A_MLSLVLREM] = { .type = NLA_U32 },
69         [NLBL_CIPSOV4_A_MLSLVL] = { .type = NLA_NESTED },
70         [NLBL_CIPSOV4_A_MLSLVLLST] = { .type = NLA_NESTED },
71         [NLBL_CIPSOV4_A_MLSCATLOC] = { .type = NLA_U32 },
72         [NLBL_CIPSOV4_A_MLSCATREM] = { .type = NLA_U32 },
73         [NLBL_CIPSOV4_A_MLSCAT] = { .type = NLA_NESTED },
74         [NLBL_CIPSOV4_A_MLSCATLST] = { .type = NLA_NESTED },
75 };
76
77 /*
78  * Helper Functions
79  */
80
81 /**
82  * netlbl_cipsov4_doi_free - Frees a CIPSO V4 DOI definition
83  * @entry: the entry's RCU field
84  *
85  * Description:
86  * This function is designed to be used as a callback to the call_rcu()
87  * function so that the memory allocated to the DOI definition can be released
88  * safely.
89  *
90  */
91 static void netlbl_cipsov4_doi_free(struct rcu_head *entry)
92 {
93         struct cipso_v4_doi *ptr;
94
95         ptr = container_of(entry, struct cipso_v4_doi, rcu);
96         switch (ptr->type) {
97         case CIPSO_V4_MAP_STD:
98                 kfree(ptr->map.std->lvl.cipso);
99                 kfree(ptr->map.std->lvl.local);
100                 kfree(ptr->map.std->cat.cipso);
101                 kfree(ptr->map.std->cat.local);
102                 break;
103         }
104         kfree(ptr);
105 }
106
107 /**
108  * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
109  * @info: the Generic NETLINK info block
110  * @doi_def: the CIPSO V4 DOI definition
111  *
112  * Description:
113  * Parse the common sections of a ADD message and fill in the related values
114  * in @doi_def.  Returns zero on success, negative values on failure.
115  *
116  */
117 static int netlbl_cipsov4_add_common(struct genl_info *info,
118                                      struct cipso_v4_doi *doi_def)
119 {
120         struct nlattr *nla;
121         int nla_rem;
122         u32 iter = 0;
123
124         doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
125
126         if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST],
127                                 NLBL_CIPSOV4_A_MAX,
128                                 netlbl_cipsov4_genl_policy) != 0)
129                 return -EINVAL;
130
131         nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem)
132                 if (nla->nla_type == NLBL_CIPSOV4_A_TAG) {
133                         if (iter > CIPSO_V4_TAG_MAXCNT)
134                                 return -EINVAL;
135                         doi_def->tags[iter++] = nla_get_u8(nla);
136                 }
137         if (iter < CIPSO_V4_TAG_MAXCNT)
138                 doi_def->tags[iter] = CIPSO_V4_TAG_INVALID;
139
140         return 0;
141 }
142
143 /*
144  * NetLabel Command Handlers
145  */
146
147 /**
148  * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
149  * @info: the Generic NETLINK info block
150  *
151  * Description:
152  * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message
153  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
154  * error.
155  *
156  */
157 static int netlbl_cipsov4_add_std(struct genl_info *info)
158 {
159         int ret_val = -EINVAL;
160         struct cipso_v4_doi *doi_def = NULL;
161         struct nlattr *nla_a;
162         struct nlattr *nla_b;
163         int nla_a_rem;
164         int nla_b_rem;
165
166         if (!info->attrs[NLBL_CIPSOV4_A_TAGLST] ||
167             !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST])
168                 return -EINVAL;
169
170         if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
171                                 NLBL_CIPSOV4_A_MAX,
172                                 netlbl_cipsov4_genl_policy) != 0)
173                 return -EINVAL;
174
175         doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
176         if (doi_def == NULL)
177                 return -ENOMEM;
178         doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL);
179         if (doi_def->map.std == NULL) {
180                 ret_val = -ENOMEM;
181                 goto add_std_failure;
182         }
183         doi_def->type = CIPSO_V4_MAP_STD;
184
185         ret_val = netlbl_cipsov4_add_common(info, doi_def);
186         if (ret_val != 0)
187                 goto add_std_failure;
188
189         nla_for_each_nested(nla_a,
190                             info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
191                             nla_a_rem)
192                 if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSLVL) {
193                         nla_for_each_nested(nla_b, nla_a, nla_b_rem)
194                                 switch (nla_b->nla_type) {
195                                 case NLBL_CIPSOV4_A_MLSLVLLOC:
196                                         if (nla_get_u32(nla_b) >=
197                                             doi_def->map.std->lvl.local_size)
198                                              doi_def->map.std->lvl.local_size =
199                                                      nla_get_u32(nla_b) + 1;
200                                         break;
201                                 case NLBL_CIPSOV4_A_MLSLVLREM:
202                                         if (nla_get_u32(nla_b) >=
203                                             doi_def->map.std->lvl.cipso_size)
204                                              doi_def->map.std->lvl.cipso_size =
205                                                      nla_get_u32(nla_b) + 1;
206                                         break;
207                                 }
208                 }
209         if (doi_def->map.std->lvl.local_size > CIPSO_V4_MAX_LOC_LVLS ||
210             doi_def->map.std->lvl.cipso_size > CIPSO_V4_MAX_REM_LVLS)
211                 goto add_std_failure;
212         doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size,
213                                               sizeof(u32),
214                                               GFP_KERNEL);
215         if (doi_def->map.std->lvl.local == NULL) {
216                 ret_val = -ENOMEM;
217                 goto add_std_failure;
218         }
219         doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size,
220                                               sizeof(u32),
221                                               GFP_KERNEL);
222         if (doi_def->map.std->lvl.cipso == NULL) {
223                 ret_val = -ENOMEM;
224                 goto add_std_failure;
225         }
226         nla_for_each_nested(nla_a,
227                             info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
228                             nla_a_rem)
229                 if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSLVL) {
230                         struct nlattr *lvl_loc;
231                         struct nlattr *lvl_rem;
232
233                         if (nla_validate_nested(nla_a,
234                                               NLBL_CIPSOV4_A_MAX,
235                                               netlbl_cipsov4_genl_policy) != 0)
236                                 goto add_std_failure;
237
238                         lvl_loc = nla_find_nested(nla_a,
239                                                   NLBL_CIPSOV4_A_MLSLVLLOC);
240                         lvl_rem = nla_find_nested(nla_a,
241                                                   NLBL_CIPSOV4_A_MLSLVLREM);
242                         if (lvl_loc == NULL || lvl_rem == NULL)
243                                 goto add_std_failure;
244                         doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] =
245                                 nla_get_u32(lvl_rem);
246                         doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] =
247                                 nla_get_u32(lvl_loc);
248                 }
249
250         if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) {
251                 if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
252                                         NLBL_CIPSOV4_A_MAX,
253                                         netlbl_cipsov4_genl_policy) != 0)
254                         goto add_std_failure;
255
256                 nla_for_each_nested(nla_a,
257                                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
258                                     nla_a_rem)
259                         if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSCAT) {
260                                 if (nla_validate_nested(nla_a,
261                                               NLBL_CIPSOV4_A_MAX,
262                                               netlbl_cipsov4_genl_policy) != 0)
263                                         goto add_std_failure;
264                                 nla_for_each_nested(nla_b, nla_a, nla_b_rem)
265                                         switch (nla_b->nla_type) {
266                                         case NLBL_CIPSOV4_A_MLSCATLOC:
267                                                 if (nla_get_u32(nla_b) >=
268                                               doi_def->map.std->cat.local_size)
269                                              doi_def->map.std->cat.local_size =
270                                                      nla_get_u32(nla_b) + 1;
271                                                 break;
272                                         case NLBL_CIPSOV4_A_MLSCATREM:
273                                                 if (nla_get_u32(nla_b) >=
274                                               doi_def->map.std->cat.cipso_size)
275                                              doi_def->map.std->cat.cipso_size =
276                                                      nla_get_u32(nla_b) + 1;
277                                                 break;
278                                         }
279                         }
280                 if (doi_def->map.std->cat.local_size > CIPSO_V4_MAX_LOC_CATS ||
281                     doi_def->map.std->cat.cipso_size > CIPSO_V4_MAX_REM_CATS)
282                         goto add_std_failure;
283                 doi_def->map.std->cat.local = kcalloc(
284                                               doi_def->map.std->cat.local_size,
285                                               sizeof(u32),
286                                               GFP_KERNEL);
287                 if (doi_def->map.std->cat.local == NULL) {
288                         ret_val = -ENOMEM;
289                         goto add_std_failure;
290                 }
291                 doi_def->map.std->cat.cipso = kcalloc(
292                                               doi_def->map.std->cat.cipso_size,
293                                               sizeof(u32),
294                                               GFP_KERNEL);
295                 if (doi_def->map.std->cat.cipso == NULL) {
296                         ret_val = -ENOMEM;
297                         goto add_std_failure;
298                 }
299                 nla_for_each_nested(nla_a,
300                                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
301                                     nla_a_rem)
302                         if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSCAT) {
303                                 struct nlattr *cat_loc;
304                                 struct nlattr *cat_rem;
305
306                                 cat_loc = nla_find_nested(nla_a,
307                                                      NLBL_CIPSOV4_A_MLSCATLOC);
308                                 cat_rem = nla_find_nested(nla_a,
309                                                      NLBL_CIPSOV4_A_MLSCATREM);
310                                 if (cat_loc == NULL || cat_rem == NULL)
311                                         goto add_std_failure;
312                                 doi_def->map.std->cat.local[
313                                                         nla_get_u32(cat_loc)] =
314                                         nla_get_u32(cat_rem);
315                                 doi_def->map.std->cat.cipso[
316                                                         nla_get_u32(cat_rem)] =
317                                         nla_get_u32(cat_loc);
318                         }
319         }
320
321         ret_val = cipso_v4_doi_add(doi_def);
322         if (ret_val != 0)
323                 goto add_std_failure;
324         return 0;
325
326 add_std_failure:
327         if (doi_def)
328                 netlbl_cipsov4_doi_free(&doi_def->rcu);
329         return ret_val;
330 }
331
332 /**
333  * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
334  * @info: the Generic NETLINK info block
335  *
336  * Description:
337  * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
338  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
339  * error.
340  *
341  */
342 static int netlbl_cipsov4_add_pass(struct genl_info *info)
343 {
344         int ret_val;
345         struct cipso_v4_doi *doi_def = NULL;
346
347         if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
348                 return -EINVAL;
349
350         doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
351         if (doi_def == NULL)
352                 return -ENOMEM;
353         doi_def->type = CIPSO_V4_MAP_PASS;
354
355         ret_val = netlbl_cipsov4_add_common(info, doi_def);
356         if (ret_val != 0)
357                 goto add_pass_failure;
358
359         ret_val = cipso_v4_doi_add(doi_def);
360         if (ret_val != 0)
361                 goto add_pass_failure;
362         return 0;
363
364 add_pass_failure:
365         netlbl_cipsov4_doi_free(&doi_def->rcu);
366         return ret_val;
367 }
368
369 /**
370  * netlbl_cipsov4_add - Handle an ADD message
371  * @skb: the NETLINK buffer
372  * @info: the Generic NETLINK info block
373  *
374  * Description:
375  * Create a new DOI definition based on the given ADD message and add it to the
376  * CIPSO V4 engine.  Returns zero on success, negative values on failure.
377  *
378  */
379 static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
380
381 {
382         int ret_val = -EINVAL;
383         u32 type;
384         u32 doi;
385         const char *type_str = "(unknown)";
386         struct audit_buffer *audit_buf;
387         struct netlbl_audit audit_info;
388
389         if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
390             !info->attrs[NLBL_CIPSOV4_A_MTYPE])
391                 return -EINVAL;
392
393         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
394         netlbl_netlink_auditinfo(skb, &audit_info);
395
396         type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
397         switch (type) {
398         case CIPSO_V4_MAP_STD:
399                 type_str = "std";
400                 ret_val = netlbl_cipsov4_add_std(info);
401                 break;
402         case CIPSO_V4_MAP_PASS:
403                 type_str = "pass";
404                 ret_val = netlbl_cipsov4_add_pass(info);
405                 break;
406         }
407
408         audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
409                                               &audit_info);
410         audit_log_format(audit_buf,
411                          " cipso_doi=%u cipso_type=%s res=%u",
412                          doi,
413                          type_str,
414                          ret_val == 0 ? 1 : 0);
415         audit_log_end(audit_buf);
416
417         return ret_val;
418 }
419
420 /**
421  * netlbl_cipsov4_list - Handle a LIST message
422  * @skb: the NETLINK buffer
423  * @info: the Generic NETLINK info block
424  *
425  * Description:
426  * Process a user generated LIST message and respond accordingly.  While the
427  * response message generated by the kernel is straightforward, determining
428  * before hand the size of the buffer to allocate is not (we have to generate
429  * the message to know the size).  In order to keep this function sane what we
430  * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in
431  * that size, if we fail then we restart with a larger buffer and try again.
432  * We continue in this manner until we hit a limit of failed attempts then we
433  * give up and just send an error message.  Returns zero on success and
434  * negative values on error.
435  *
436  */
437 static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
438 {
439         int ret_val;
440         struct sk_buff *ans_skb = NULL;
441         u32 nlsze_mult = 1;
442         void *data;
443         u32 doi;
444         struct nlattr *nla_a;
445         struct nlattr *nla_b;
446         struct cipso_v4_doi *doi_def;
447         u32 iter;
448
449         if (!info->attrs[NLBL_CIPSOV4_A_DOI]) {
450                 ret_val = -EINVAL;
451                 goto list_failure;
452         }
453
454 list_start:
455         ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE * nlsze_mult, GFP_KERNEL);
456         if (ans_skb == NULL) {
457                 ret_val = -ENOMEM;
458                 goto list_failure;
459         }
460         data = netlbl_netlink_hdr_put(ans_skb,
461                                       info->snd_pid,
462                                       info->snd_seq,
463                                       netlbl_cipsov4_gnl_family.id,
464                                       0,
465                                       NLBL_CIPSOV4_C_LIST);
466         if (data == NULL) {
467                 ret_val = -ENOMEM;
468                 goto list_failure;
469         }
470
471         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
472
473         rcu_read_lock();
474         doi_def = cipso_v4_doi_getdef(doi);
475         if (doi_def == NULL) {
476                 ret_val = -EINVAL;
477                 goto list_failure;
478         }
479
480         ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
481         if (ret_val != 0)
482                 goto list_failure_lock;
483
484         nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_TAGLST);
485         if (nla_a == NULL) {
486                 ret_val = -ENOMEM;
487                 goto list_failure_lock;
488         }
489         for (iter = 0;
490              iter < CIPSO_V4_TAG_MAXCNT &&
491                doi_def->tags[iter] != CIPSO_V4_TAG_INVALID;
492              iter++) {
493                 ret_val = nla_put_u8(ans_skb,
494                                      NLBL_CIPSOV4_A_TAG,
495                                      doi_def->tags[iter]);
496                 if (ret_val != 0)
497                         goto list_failure_lock;
498         }
499         nla_nest_end(ans_skb, nla_a);
500
501         switch (doi_def->type) {
502         case CIPSO_V4_MAP_STD:
503                 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
504                 if (nla_a == NULL) {
505                         ret_val = -ENOMEM;
506                         goto list_failure_lock;
507                 }
508                 for (iter = 0;
509                      iter < doi_def->map.std->lvl.local_size;
510                      iter++) {
511                         if (doi_def->map.std->lvl.local[iter] ==
512                             CIPSO_V4_INV_LVL)
513                                 continue;
514
515                         nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVL);
516                         if (nla_b == NULL) {
517                                 ret_val = -ENOMEM;
518                                 goto list_retry;
519                         }
520                         ret_val = nla_put_u32(ans_skb,
521                                               NLBL_CIPSOV4_A_MLSLVLLOC,
522                                               iter);
523                         if (ret_val != 0)
524                                 goto list_retry;
525                         ret_val = nla_put_u32(ans_skb,
526                                             NLBL_CIPSOV4_A_MLSLVLREM,
527                                             doi_def->map.std->lvl.local[iter]);
528                         if (ret_val != 0)
529                                 goto list_retry;
530                         nla_nest_end(ans_skb, nla_b);
531                 }
532                 nla_nest_end(ans_skb, nla_a);
533
534                 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCATLST);
535                 if (nla_a == NULL) {
536                         ret_val = -ENOMEM;
537                         goto list_retry;
538                 }
539                 for (iter = 0;
540                      iter < doi_def->map.std->cat.local_size;
541                      iter++) {
542                         if (doi_def->map.std->cat.local[iter] ==
543                             CIPSO_V4_INV_CAT)
544                                 continue;
545
546                         nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCAT);
547                         if (nla_b == NULL) {
548                                 ret_val = -ENOMEM;
549                                 goto list_retry;
550                         }
551                         ret_val = nla_put_u32(ans_skb,
552                                               NLBL_CIPSOV4_A_MLSCATLOC,
553                                               iter);
554                         if (ret_val != 0)
555                                 goto list_retry;
556                         ret_val = nla_put_u32(ans_skb,
557                                             NLBL_CIPSOV4_A_MLSCATREM,
558                                             doi_def->map.std->cat.local[iter]);
559                         if (ret_val != 0)
560                                 goto list_retry;
561                         nla_nest_end(ans_skb, nla_b);
562                 }
563                 nla_nest_end(ans_skb, nla_a);
564
565                 break;
566         }
567         rcu_read_unlock();
568
569         genlmsg_end(ans_skb, data);
570
571         ret_val = genlmsg_unicast(ans_skb, info->snd_pid);
572         if (ret_val != 0)
573                 goto list_failure;
574
575         return 0;
576
577 list_retry:
578         /* XXX - this limit is a guesstimate */
579         if (nlsze_mult < 4) {
580                 rcu_read_unlock();
581                 kfree_skb(ans_skb);
582                 nlsze_mult++;
583                 goto list_start;
584         }
585 list_failure_lock:
586         rcu_read_unlock();
587 list_failure:
588         kfree_skb(ans_skb);
589         return ret_val;
590 }
591
592 /**
593  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
594  * @doi_def: the CIPSOv4 DOI definition
595  * @arg: the netlbl_cipsov4_doiwalk_arg structure
596  *
597  * Description:
598  * This function is designed to be used as a callback to the
599  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
600  * message.  Returns the size of the message on success, negative values on
601  * failure.
602  *
603  */
604 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
605 {
606         int ret_val = -ENOMEM;
607         struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
608         void *data;
609
610         data = netlbl_netlink_hdr_put(cb_arg->skb,
611                                       NETLINK_CB(cb_arg->nl_cb->skb).pid,
612                                       cb_arg->seq,
613                                       netlbl_cipsov4_gnl_family.id,
614                                       NLM_F_MULTI,
615                                       NLBL_CIPSOV4_C_LISTALL);
616         if (data == NULL)
617                 goto listall_cb_failure;
618
619         ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
620         if (ret_val != 0)
621                 goto listall_cb_failure;
622         ret_val = nla_put_u32(cb_arg->skb,
623                               NLBL_CIPSOV4_A_MTYPE,
624                               doi_def->type);
625         if (ret_val != 0)
626                 goto listall_cb_failure;
627
628         return genlmsg_end(cb_arg->skb, data);
629
630 listall_cb_failure:
631         genlmsg_cancel(cb_arg->skb, data);
632         return ret_val;
633 }
634
635 /**
636  * netlbl_cipsov4_listall - Handle a LISTALL message
637  * @skb: the NETLINK buffer
638  * @cb: the NETLINK callback
639  *
640  * Description:
641  * Process a user generated LISTALL message and respond accordingly.  Returns
642  * zero on success and negative values on error.
643  *
644  */
645 static int netlbl_cipsov4_listall(struct sk_buff *skb,
646                                   struct netlink_callback *cb)
647 {
648         struct netlbl_cipsov4_doiwalk_arg cb_arg;
649         int doi_skip = cb->args[0];
650
651         cb_arg.nl_cb = cb;
652         cb_arg.skb = skb;
653         cb_arg.seq = cb->nlh->nlmsg_seq;
654
655         cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
656
657         cb->args[0] = doi_skip;
658         return skb->len;
659 }
660
661 /**
662  * netlbl_cipsov4_remove - Handle a REMOVE message
663  * @skb: the NETLINK buffer
664  * @info: the Generic NETLINK info block
665  *
666  * Description:
667  * Process a user generated REMOVE message and respond accordingly.  Returns
668  * zero on success, negative values on failure.
669  *
670  */
671 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
672 {
673         int ret_val = -EINVAL;
674         u32 doi = 0;
675         struct audit_buffer *audit_buf;
676         struct netlbl_audit audit_info;
677
678         if (!info->attrs[NLBL_CIPSOV4_A_DOI])
679                 return -EINVAL;
680
681         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
682         netlbl_netlink_auditinfo(skb, &audit_info);
683
684         ret_val = cipso_v4_doi_remove(doi,
685                                       &audit_info,
686                                       netlbl_cipsov4_doi_free);
687
688         audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
689                                               &audit_info);
690         audit_log_format(audit_buf,
691                          " cipso_doi=%u res=%u",
692                          doi,
693                          ret_val == 0 ? 1 : 0);
694         audit_log_end(audit_buf);
695
696         return ret_val;
697 }
698
699 /*
700  * NetLabel Generic NETLINK Command Definitions
701  */
702
703 static struct genl_ops netlbl_cipsov4_genl_c_add = {
704         .cmd = NLBL_CIPSOV4_C_ADD,
705         .flags = GENL_ADMIN_PERM,
706         .policy = netlbl_cipsov4_genl_policy,
707         .doit = netlbl_cipsov4_add,
708         .dumpit = NULL,
709 };
710
711 static struct genl_ops netlbl_cipsov4_genl_c_remove = {
712         .cmd = NLBL_CIPSOV4_C_REMOVE,
713         .flags = GENL_ADMIN_PERM,
714         .policy = netlbl_cipsov4_genl_policy,
715         .doit = netlbl_cipsov4_remove,
716         .dumpit = NULL,
717 };
718
719 static struct genl_ops netlbl_cipsov4_genl_c_list = {
720         .cmd = NLBL_CIPSOV4_C_LIST,
721         .flags = 0,
722         .policy = netlbl_cipsov4_genl_policy,
723         .doit = netlbl_cipsov4_list,
724         .dumpit = NULL,
725 };
726
727 static struct genl_ops netlbl_cipsov4_genl_c_listall = {
728         .cmd = NLBL_CIPSOV4_C_LISTALL,
729         .flags = 0,
730         .policy = netlbl_cipsov4_genl_policy,
731         .doit = NULL,
732         .dumpit = netlbl_cipsov4_listall,
733 };
734
735 /*
736  * NetLabel Generic NETLINK Protocol Functions
737  */
738
739 /**
740  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
741  *
742  * Description:
743  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
744  * mechanism.  Returns zero on success, negative values on failure.
745  *
746  */
747 int netlbl_cipsov4_genl_init(void)
748 {
749         int ret_val;
750
751         ret_val = genl_register_family(&netlbl_cipsov4_gnl_family);
752         if (ret_val != 0)
753                 return ret_val;
754
755         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
756                                     &netlbl_cipsov4_genl_c_add);
757         if (ret_val != 0)
758                 return ret_val;
759         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
760                                     &netlbl_cipsov4_genl_c_remove);
761         if (ret_val != 0)
762                 return ret_val;
763         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
764                                     &netlbl_cipsov4_genl_c_list);
765         if (ret_val != 0)
766                 return ret_val;
767         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
768                                     &netlbl_cipsov4_genl_c_listall);
769         if (ret_val != 0)
770                 return ret_val;
771
772         return 0;
773 }