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