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