[NETPOLL]: Make sure TX lock is taken with BH disabled.
[linux-2.6] / 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         if (audit_buf != NULL) {
411                 audit_log_format(audit_buf,
412                                  " cipso_doi=%u cipso_type=%s res=%u",
413                                  doi,
414                                  type_str,
415                                  ret_val == 0 ? 1 : 0);
416                 audit_log_end(audit_buf);
417         }
418
419         return ret_val;
420 }
421
422 /**
423  * netlbl_cipsov4_list - Handle a LIST message
424  * @skb: the NETLINK buffer
425  * @info: the Generic NETLINK info block
426  *
427  * Description:
428  * Process a user generated LIST message and respond accordingly.  While the
429  * response message generated by the kernel is straightforward, determining
430  * before hand the size of the buffer to allocate is not (we have to generate
431  * the message to know the size).  In order to keep this function sane what we
432  * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in
433  * that size, if we fail then we restart with a larger buffer and try again.
434  * We continue in this manner until we hit a limit of failed attempts then we
435  * give up and just send an error message.  Returns zero on success and
436  * negative values on error.
437  *
438  */
439 static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
440 {
441         int ret_val;
442         struct sk_buff *ans_skb = NULL;
443         u32 nlsze_mult = 1;
444         void *data;
445         u32 doi;
446         struct nlattr *nla_a;
447         struct nlattr *nla_b;
448         struct cipso_v4_doi *doi_def;
449         u32 iter;
450
451         if (!info->attrs[NLBL_CIPSOV4_A_DOI]) {
452                 ret_val = -EINVAL;
453                 goto list_failure;
454         }
455
456 list_start:
457         ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE * nlsze_mult, GFP_KERNEL);
458         if (ans_skb == NULL) {
459                 ret_val = -ENOMEM;
460                 goto list_failure;
461         }
462         data = genlmsg_put_reply(ans_skb, info, &netlbl_cipsov4_gnl_family,
463                                  0, NLBL_CIPSOV4_C_LIST);
464         if (data == NULL) {
465                 ret_val = -ENOMEM;
466                 goto list_failure;
467         }
468
469         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
470
471         rcu_read_lock();
472         doi_def = cipso_v4_doi_getdef(doi);
473         if (doi_def == NULL) {
474                 ret_val = -EINVAL;
475                 goto list_failure;
476         }
477
478         ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
479         if (ret_val != 0)
480                 goto list_failure_lock;
481
482         nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_TAGLST);
483         if (nla_a == NULL) {
484                 ret_val = -ENOMEM;
485                 goto list_failure_lock;
486         }
487         for (iter = 0;
488              iter < CIPSO_V4_TAG_MAXCNT &&
489                doi_def->tags[iter] != CIPSO_V4_TAG_INVALID;
490              iter++) {
491                 ret_val = nla_put_u8(ans_skb,
492                                      NLBL_CIPSOV4_A_TAG,
493                                      doi_def->tags[iter]);
494                 if (ret_val != 0)
495                         goto list_failure_lock;
496         }
497         nla_nest_end(ans_skb, nla_a);
498
499         switch (doi_def->type) {
500         case CIPSO_V4_MAP_STD:
501                 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
502                 if (nla_a == NULL) {
503                         ret_val = -ENOMEM;
504                         goto list_failure_lock;
505                 }
506                 for (iter = 0;
507                      iter < doi_def->map.std->lvl.local_size;
508                      iter++) {
509                         if (doi_def->map.std->lvl.local[iter] ==
510                             CIPSO_V4_INV_LVL)
511                                 continue;
512
513                         nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVL);
514                         if (nla_b == NULL) {
515                                 ret_val = -ENOMEM;
516                                 goto list_retry;
517                         }
518                         ret_val = nla_put_u32(ans_skb,
519                                               NLBL_CIPSOV4_A_MLSLVLLOC,
520                                               iter);
521                         if (ret_val != 0)
522                                 goto list_retry;
523                         ret_val = nla_put_u32(ans_skb,
524                                             NLBL_CIPSOV4_A_MLSLVLREM,
525                                             doi_def->map.std->lvl.local[iter]);
526                         if (ret_val != 0)
527                                 goto list_retry;
528                         nla_nest_end(ans_skb, nla_b);
529                 }
530                 nla_nest_end(ans_skb, nla_a);
531
532                 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCATLST);
533                 if (nla_a == NULL) {
534                         ret_val = -ENOMEM;
535                         goto list_retry;
536                 }
537                 for (iter = 0;
538                      iter < doi_def->map.std->cat.local_size;
539                      iter++) {
540                         if (doi_def->map.std->cat.local[iter] ==
541                             CIPSO_V4_INV_CAT)
542                                 continue;
543
544                         nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCAT);
545                         if (nla_b == NULL) {
546                                 ret_val = -ENOMEM;
547                                 goto list_retry;
548                         }
549                         ret_val = nla_put_u32(ans_skb,
550                                               NLBL_CIPSOV4_A_MLSCATLOC,
551                                               iter);
552                         if (ret_val != 0)
553                                 goto list_retry;
554                         ret_val = nla_put_u32(ans_skb,
555                                             NLBL_CIPSOV4_A_MLSCATREM,
556                                             doi_def->map.std->cat.local[iter]);
557                         if (ret_val != 0)
558                                 goto list_retry;
559                         nla_nest_end(ans_skb, nla_b);
560                 }
561                 nla_nest_end(ans_skb, nla_a);
562
563                 break;
564         }
565         rcu_read_unlock();
566
567         genlmsg_end(ans_skb, data);
568
569         ret_val = genlmsg_reply(ans_skb, info);
570         if (ret_val != 0)
571                 goto list_failure;
572
573         return 0;
574
575 list_retry:
576         /* XXX - this limit is a guesstimate */
577         if (nlsze_mult < 4) {
578                 rcu_read_unlock();
579                 kfree_skb(ans_skb);
580                 nlsze_mult++;
581                 goto list_start;
582         }
583 list_failure_lock:
584         rcu_read_unlock();
585 list_failure:
586         kfree_skb(ans_skb);
587         return ret_val;
588 }
589
590 /**
591  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
592  * @doi_def: the CIPSOv4 DOI definition
593  * @arg: the netlbl_cipsov4_doiwalk_arg structure
594  *
595  * Description:
596  * This function is designed to be used as a callback to the
597  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
598  * message.  Returns the size of the message on success, negative values on
599  * failure.
600  *
601  */
602 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
603 {
604         int ret_val = -ENOMEM;
605         struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
606         void *data;
607
608         data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid,
609                            cb_arg->seq, &netlbl_cipsov4_gnl_family,
610                            NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
611         if (data == NULL)
612                 goto listall_cb_failure;
613
614         ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
615         if (ret_val != 0)
616                 goto listall_cb_failure;
617         ret_val = nla_put_u32(cb_arg->skb,
618                               NLBL_CIPSOV4_A_MTYPE,
619                               doi_def->type);
620         if (ret_val != 0)
621                 goto listall_cb_failure;
622
623         return genlmsg_end(cb_arg->skb, data);
624
625 listall_cb_failure:
626         genlmsg_cancel(cb_arg->skb, data);
627         return ret_val;
628 }
629
630 /**
631  * netlbl_cipsov4_listall - Handle a LISTALL message
632  * @skb: the NETLINK buffer
633  * @cb: the NETLINK callback
634  *
635  * Description:
636  * Process a user generated LISTALL message and respond accordingly.  Returns
637  * zero on success and negative values on error.
638  *
639  */
640 static int netlbl_cipsov4_listall(struct sk_buff *skb,
641                                   struct netlink_callback *cb)
642 {
643         struct netlbl_cipsov4_doiwalk_arg cb_arg;
644         int doi_skip = cb->args[0];
645
646         cb_arg.nl_cb = cb;
647         cb_arg.skb = skb;
648         cb_arg.seq = cb->nlh->nlmsg_seq;
649
650         cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
651
652         cb->args[0] = doi_skip;
653         return skb->len;
654 }
655
656 /**
657  * netlbl_cipsov4_remove - Handle a REMOVE message
658  * @skb: the NETLINK buffer
659  * @info: the Generic NETLINK info block
660  *
661  * Description:
662  * Process a user generated REMOVE message and respond accordingly.  Returns
663  * zero on success, negative values on failure.
664  *
665  */
666 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
667 {
668         int ret_val = -EINVAL;
669         u32 doi = 0;
670         struct audit_buffer *audit_buf;
671         struct netlbl_audit audit_info;
672
673         if (!info->attrs[NLBL_CIPSOV4_A_DOI])
674                 return -EINVAL;
675
676         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
677         netlbl_netlink_auditinfo(skb, &audit_info);
678
679         ret_val = cipso_v4_doi_remove(doi,
680                                       &audit_info,
681                                       netlbl_cipsov4_doi_free);
682
683         audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
684                                               &audit_info);
685         if (audit_buf != NULL) {
686                 audit_log_format(audit_buf,
687                                  " cipso_doi=%u res=%u",
688                                  doi,
689                                  ret_val == 0 ? 1 : 0);
690                 audit_log_end(audit_buf);
691         }
692
693         return ret_val;
694 }
695
696 /*
697  * NetLabel Generic NETLINK Command Definitions
698  */
699
700 static struct genl_ops netlbl_cipsov4_genl_c_add = {
701         .cmd = NLBL_CIPSOV4_C_ADD,
702         .flags = GENL_ADMIN_PERM,
703         .policy = netlbl_cipsov4_genl_policy,
704         .doit = netlbl_cipsov4_add,
705         .dumpit = NULL,
706 };
707
708 static struct genl_ops netlbl_cipsov4_genl_c_remove = {
709         .cmd = NLBL_CIPSOV4_C_REMOVE,
710         .flags = GENL_ADMIN_PERM,
711         .policy = netlbl_cipsov4_genl_policy,
712         .doit = netlbl_cipsov4_remove,
713         .dumpit = NULL,
714 };
715
716 static struct genl_ops netlbl_cipsov4_genl_c_list = {
717         .cmd = NLBL_CIPSOV4_C_LIST,
718         .flags = 0,
719         .policy = netlbl_cipsov4_genl_policy,
720         .doit = netlbl_cipsov4_list,
721         .dumpit = NULL,
722 };
723
724 static struct genl_ops netlbl_cipsov4_genl_c_listall = {
725         .cmd = NLBL_CIPSOV4_C_LISTALL,
726         .flags = 0,
727         .policy = netlbl_cipsov4_genl_policy,
728         .doit = NULL,
729         .dumpit = netlbl_cipsov4_listall,
730 };
731
732 /*
733  * NetLabel Generic NETLINK Protocol Functions
734  */
735
736 /**
737  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
738  *
739  * Description:
740  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
741  * mechanism.  Returns zero on success, negative values on failure.
742  *
743  */
744 int netlbl_cipsov4_genl_init(void)
745 {
746         int ret_val;
747
748         ret_val = genl_register_family(&netlbl_cipsov4_gnl_family);
749         if (ret_val != 0)
750                 return ret_val;
751
752         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
753                                     &netlbl_cipsov4_genl_c_add);
754         if (ret_val != 0)
755                 return ret_val;
756         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
757                                     &netlbl_cipsov4_genl_c_remove);
758         if (ret_val != 0)
759                 return ret_val;
760         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
761                                     &netlbl_cipsov4_genl_c_list);
762         if (ret_val != 0)
763                 return ret_val;
764         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
765                                     &netlbl_cipsov4_genl_c_listall);
766         if (ret_val != 0)
767                 return ret_val;
768
769         return 0;
770 }