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