Merge tag 'fscrypt_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[sfrench/cifs-2.6.git] / net / netlabel / netlabel_cipso_v4.c
1 /*
2  * NetLabel CIPSO/IPv4 Support
3  *
4  * This file defines the CIPSO/IPv4 functions for the NetLabel system.  The
5  * NetLabel system manages static and dynamic label mappings for network
6  * protocols such as CIPSO and RIPSO.
7  *
8  * Author: Paul Moore <paul@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, NULL) != 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, NULL) != 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, NLBL_CIPSOV4_A_MAX,
174                                                 netlbl_cipsov4_genl_policy,
175                                                 NULL) != 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, NULL) != 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,
249                                                         NULL) != 0)
250                                         goto add_std_failure;
251                                 nla_for_each_nested(nla_b, nla_a, nla_b_rem)
252                                         switch (nla_type(nla_b)) {
253                                         case NLBL_CIPSOV4_A_MLSCATLOC:
254                                                 if (nla_get_u32(nla_b) >
255                                                     CIPSO_V4_MAX_LOC_CATS)
256                                                         goto add_std_failure;
257                                                 if (nla_get_u32(nla_b) >=
258                                               doi_def->map.std->cat.local_size)
259                                              doi_def->map.std->cat.local_size =
260                                                      nla_get_u32(nla_b) + 1;
261                                                 break;
262                                         case NLBL_CIPSOV4_A_MLSCATREM:
263                                                 if (nla_get_u32(nla_b) >
264                                                     CIPSO_V4_MAX_REM_CATS)
265                                                         goto add_std_failure;
266                                                 if (nla_get_u32(nla_b) >=
267                                               doi_def->map.std->cat.cipso_size)
268                                              doi_def->map.std->cat.cipso_size =
269                                                      nla_get_u32(nla_b) + 1;
270                                                 break;
271                                         }
272                         }
273                 doi_def->map.std->cat.local = kcalloc(
274                                               doi_def->map.std->cat.local_size,
275                                               sizeof(u32),
276                                               GFP_KERNEL);
277                 if (doi_def->map.std->cat.local == NULL) {
278                         ret_val = -ENOMEM;
279                         goto add_std_failure;
280                 }
281                 doi_def->map.std->cat.cipso = kcalloc(
282                                               doi_def->map.std->cat.cipso_size,
283                                               sizeof(u32),
284                                               GFP_KERNEL);
285                 if (doi_def->map.std->cat.cipso == NULL) {
286                         ret_val = -ENOMEM;
287                         goto add_std_failure;
288                 }
289                 for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++)
290                         doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT;
291                 for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++)
292                         doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT;
293                 nla_for_each_nested(nla_a,
294                                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
295                                     nla_a_rem)
296                         if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
297                                 struct nlattr *cat_loc;
298                                 struct nlattr *cat_rem;
299
300                                 cat_loc = nla_find_nested(nla_a,
301                                                      NLBL_CIPSOV4_A_MLSCATLOC);
302                                 cat_rem = nla_find_nested(nla_a,
303                                                      NLBL_CIPSOV4_A_MLSCATREM);
304                                 if (cat_loc == NULL || cat_rem == NULL)
305                                         goto add_std_failure;
306                                 doi_def->map.std->cat.local[
307                                                         nla_get_u32(cat_loc)] =
308                                         nla_get_u32(cat_rem);
309                                 doi_def->map.std->cat.cipso[
310                                                         nla_get_u32(cat_rem)] =
311                                         nla_get_u32(cat_loc);
312                         }
313         }
314
315         ret_val = cipso_v4_doi_add(doi_def, audit_info);
316         if (ret_val != 0)
317                 goto add_std_failure;
318         return 0;
319
320 add_std_failure:
321         cipso_v4_doi_free(doi_def);
322         return ret_val;
323 }
324
325 /**
326  * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
327  * @info: the Generic NETLINK info block
328  * @audit_info: NetLabel audit information
329  *
330  * Description:
331  * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
332  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
333  * error.
334  *
335  */
336 static int netlbl_cipsov4_add_pass(struct genl_info *info,
337                                    struct netlbl_audit *audit_info)
338 {
339         int ret_val;
340         struct cipso_v4_doi *doi_def = NULL;
341
342         if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
343                 return -EINVAL;
344
345         doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
346         if (doi_def == NULL)
347                 return -ENOMEM;
348         doi_def->type = CIPSO_V4_MAP_PASS;
349
350         ret_val = netlbl_cipsov4_add_common(info, doi_def);
351         if (ret_val != 0)
352                 goto add_pass_failure;
353
354         ret_val = cipso_v4_doi_add(doi_def, audit_info);
355         if (ret_val != 0)
356                 goto add_pass_failure;
357         return 0;
358
359 add_pass_failure:
360         cipso_v4_doi_free(doi_def);
361         return ret_val;
362 }
363
364 /**
365  * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
366  * @info: the Generic NETLINK info block
367  * @audit_info: NetLabel audit information
368  *
369  * Description:
370  * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
371  * message and add it to the CIPSO V4 engine.  Return zero on success and
372  * non-zero on error.
373  *
374  */
375 static int netlbl_cipsov4_add_local(struct genl_info *info,
376                                     struct netlbl_audit *audit_info)
377 {
378         int ret_val;
379         struct cipso_v4_doi *doi_def = NULL;
380
381         if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
382                 return -EINVAL;
383
384         doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
385         if (doi_def == NULL)
386                 return -ENOMEM;
387         doi_def->type = CIPSO_V4_MAP_LOCAL;
388
389         ret_val = netlbl_cipsov4_add_common(info, doi_def);
390         if (ret_val != 0)
391                 goto add_local_failure;
392
393         ret_val = cipso_v4_doi_add(doi_def, audit_info);
394         if (ret_val != 0)
395                 goto add_local_failure;
396         return 0;
397
398 add_local_failure:
399         cipso_v4_doi_free(doi_def);
400         return ret_val;
401 }
402
403 /**
404  * netlbl_cipsov4_add - Handle an ADD message
405  * @skb: the NETLINK buffer
406  * @info: the Generic NETLINK info block
407  *
408  * Description:
409  * Create a new DOI definition based on the given ADD message and add it to the
410  * CIPSO V4 engine.  Returns zero on success, negative values on failure.
411  *
412  */
413 static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
414
415 {
416         int ret_val = -EINVAL;
417         struct netlbl_audit audit_info;
418
419         if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
420             !info->attrs[NLBL_CIPSOV4_A_MTYPE])
421                 return -EINVAL;
422
423         netlbl_netlink_auditinfo(skb, &audit_info);
424         switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {
425         case CIPSO_V4_MAP_TRANS:
426                 ret_val = netlbl_cipsov4_add_std(info, &audit_info);
427                 break;
428         case CIPSO_V4_MAP_PASS:
429                 ret_val = netlbl_cipsov4_add_pass(info, &audit_info);
430                 break;
431         case CIPSO_V4_MAP_LOCAL:
432                 ret_val = netlbl_cipsov4_add_local(info, &audit_info);
433                 break;
434         }
435         if (ret_val == 0)
436                 atomic_inc(&netlabel_mgmt_protocount);
437
438         return ret_val;
439 }
440
441 /**
442  * netlbl_cipsov4_list - Handle a LIST message
443  * @skb: the NETLINK buffer
444  * @info: the Generic NETLINK info block
445  *
446  * Description:
447  * Process a user generated LIST message and respond accordingly.  While the
448  * response message generated by the kernel is straightforward, determining
449  * before hand the size of the buffer to allocate is not (we have to generate
450  * the message to know the size).  In order to keep this function sane what we
451  * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in
452  * that size, if we fail then we restart with a larger buffer and try again.
453  * We continue in this manner until we hit a limit of failed attempts then we
454  * give up and just send an error message.  Returns zero on success and
455  * negative values on error.
456  *
457  */
458 static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
459 {
460         int ret_val;
461         struct sk_buff *ans_skb = NULL;
462         u32 nlsze_mult = 1;
463         void *data;
464         u32 doi;
465         struct nlattr *nla_a;
466         struct nlattr *nla_b;
467         struct cipso_v4_doi *doi_def;
468         u32 iter;
469
470         if (!info->attrs[NLBL_CIPSOV4_A_DOI]) {
471                 ret_val = -EINVAL;
472                 goto list_failure;
473         }
474
475 list_start:
476         ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE * nlsze_mult, GFP_KERNEL);
477         if (ans_skb == NULL) {
478                 ret_val = -ENOMEM;
479                 goto list_failure;
480         }
481         data = genlmsg_put_reply(ans_skb, info, &netlbl_cipsov4_gnl_family,
482                                  0, NLBL_CIPSOV4_C_LIST);
483         if (data == NULL) {
484                 ret_val = -ENOMEM;
485                 goto list_failure;
486         }
487
488         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
489
490         rcu_read_lock();
491         doi_def = cipso_v4_doi_getdef(doi);
492         if (doi_def == NULL) {
493                 ret_val = -EINVAL;
494                 goto list_failure_lock;
495         }
496
497         ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
498         if (ret_val != 0)
499                 goto list_failure_lock;
500
501         nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_TAGLST);
502         if (nla_a == NULL) {
503                 ret_val = -ENOMEM;
504                 goto list_failure_lock;
505         }
506         for (iter = 0;
507              iter < CIPSO_V4_TAG_MAXCNT &&
508                doi_def->tags[iter] != CIPSO_V4_TAG_INVALID;
509              iter++) {
510                 ret_val = nla_put_u8(ans_skb,
511                                      NLBL_CIPSOV4_A_TAG,
512                                      doi_def->tags[iter]);
513                 if (ret_val != 0)
514                         goto list_failure_lock;
515         }
516         nla_nest_end(ans_skb, nla_a);
517
518         switch (doi_def->type) {
519         case CIPSO_V4_MAP_TRANS:
520                 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
521                 if (nla_a == NULL) {
522                         ret_val = -ENOMEM;
523                         goto list_failure_lock;
524                 }
525                 for (iter = 0;
526                      iter < doi_def->map.std->lvl.local_size;
527                      iter++) {
528                         if (doi_def->map.std->lvl.local[iter] ==
529                             CIPSO_V4_INV_LVL)
530                                 continue;
531
532                         nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVL);
533                         if (nla_b == NULL) {
534                                 ret_val = -ENOMEM;
535                                 goto list_retry;
536                         }
537                         ret_val = nla_put_u32(ans_skb,
538                                               NLBL_CIPSOV4_A_MLSLVLLOC,
539                                               iter);
540                         if (ret_val != 0)
541                                 goto list_retry;
542                         ret_val = nla_put_u32(ans_skb,
543                                             NLBL_CIPSOV4_A_MLSLVLREM,
544                                             doi_def->map.std->lvl.local[iter]);
545                         if (ret_val != 0)
546                                 goto list_retry;
547                         nla_nest_end(ans_skb, nla_b);
548                 }
549                 nla_nest_end(ans_skb, nla_a);
550
551                 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCATLST);
552                 if (nla_a == NULL) {
553                         ret_val = -ENOMEM;
554                         goto list_retry;
555                 }
556                 for (iter = 0;
557                      iter < doi_def->map.std->cat.local_size;
558                      iter++) {
559                         if (doi_def->map.std->cat.local[iter] ==
560                             CIPSO_V4_INV_CAT)
561                                 continue;
562
563                         nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCAT);
564                         if (nla_b == NULL) {
565                                 ret_val = -ENOMEM;
566                                 goto list_retry;
567                         }
568                         ret_val = nla_put_u32(ans_skb,
569                                               NLBL_CIPSOV4_A_MLSCATLOC,
570                                               iter);
571                         if (ret_val != 0)
572                                 goto list_retry;
573                         ret_val = nla_put_u32(ans_skb,
574                                             NLBL_CIPSOV4_A_MLSCATREM,
575                                             doi_def->map.std->cat.local[iter]);
576                         if (ret_val != 0)
577                                 goto list_retry;
578                         nla_nest_end(ans_skb, nla_b);
579                 }
580                 nla_nest_end(ans_skb, nla_a);
581
582                 break;
583         }
584         rcu_read_unlock();
585
586         genlmsg_end(ans_skb, data);
587         return genlmsg_reply(ans_skb, info);
588
589 list_retry:
590         /* XXX - this limit is a guesstimate */
591         if (nlsze_mult < 4) {
592                 rcu_read_unlock();
593                 kfree_skb(ans_skb);
594                 nlsze_mult *= 2;
595                 goto list_start;
596         }
597 list_failure_lock:
598         rcu_read_unlock();
599 list_failure:
600         kfree_skb(ans_skb);
601         return ret_val;
602 }
603
604 /**
605  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
606  * @doi_def: the CIPSOv4 DOI definition
607  * @arg: the netlbl_cipsov4_doiwalk_arg structure
608  *
609  * Description:
610  * This function is designed to be used as a callback to the
611  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
612  * message.  Returns the size of the message on success, negative values on
613  * failure.
614  *
615  */
616 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
617 {
618         int ret_val = -ENOMEM;
619         struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
620         void *data;
621
622         data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
623                            cb_arg->seq, &netlbl_cipsov4_gnl_family,
624                            NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
625         if (data == NULL)
626                 goto listall_cb_failure;
627
628         ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
629         if (ret_val != 0)
630                 goto listall_cb_failure;
631         ret_val = nla_put_u32(cb_arg->skb,
632                               NLBL_CIPSOV4_A_MTYPE,
633                               doi_def->type);
634         if (ret_val != 0)
635                 goto listall_cb_failure;
636
637         genlmsg_end(cb_arg->skb, data);
638         return 0;
639
640 listall_cb_failure:
641         genlmsg_cancel(cb_arg->skb, data);
642         return ret_val;
643 }
644
645 /**
646  * netlbl_cipsov4_listall - Handle a LISTALL message
647  * @skb: the NETLINK buffer
648  * @cb: the NETLINK callback
649  *
650  * Description:
651  * Process a user generated LISTALL message and respond accordingly.  Returns
652  * zero on success and negative values on error.
653  *
654  */
655 static int netlbl_cipsov4_listall(struct sk_buff *skb,
656                                   struct netlink_callback *cb)
657 {
658         struct netlbl_cipsov4_doiwalk_arg cb_arg;
659         u32 doi_skip = cb->args[0];
660
661         cb_arg.nl_cb = cb;
662         cb_arg.skb = skb;
663         cb_arg.seq = cb->nlh->nlmsg_seq;
664
665         cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
666
667         cb->args[0] = doi_skip;
668         return skb->len;
669 }
670
671 /**
672  * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE
673  * @entry: LSM domain mapping entry
674  * @arg: the netlbl_domhsh_walk_arg structure
675  *
676  * Description:
677  * This function is intended for use by netlbl_cipsov4_remove() as the callback
678  * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
679  * which are associated with the CIPSO DOI specified in @arg.  Returns zero on
680  * success, negative values on failure.
681  *
682  */
683 static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
684 {
685         struct netlbl_domhsh_walk_arg *cb_arg = arg;
686
687         if (entry->def.type == NETLBL_NLTYPE_CIPSOV4 &&
688             entry->def.cipso->doi == cb_arg->doi)
689                 return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
690
691         return 0;
692 }
693
694 /**
695  * netlbl_cipsov4_remove - Handle a REMOVE message
696  * @skb: the NETLINK buffer
697  * @info: the Generic NETLINK info block
698  *
699  * Description:
700  * Process a user generated REMOVE message and respond accordingly.  Returns
701  * zero on success, negative values on failure.
702  *
703  */
704 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
705 {
706         int ret_val = -EINVAL;
707         struct netlbl_domhsh_walk_arg cb_arg;
708         struct netlbl_audit audit_info;
709         u32 skip_bkt = 0;
710         u32 skip_chain = 0;
711
712         if (!info->attrs[NLBL_CIPSOV4_A_DOI])
713                 return -EINVAL;
714
715         netlbl_netlink_auditinfo(skb, &audit_info);
716         cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
717         cb_arg.audit_info = &audit_info;
718         ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
719                                      netlbl_cipsov4_remove_cb, &cb_arg);
720         if (ret_val == 0 || ret_val == -ENOENT) {
721                 ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info);
722                 if (ret_val == 0)
723                         atomic_dec(&netlabel_mgmt_protocount);
724         }
725
726         return ret_val;
727 }
728
729 /*
730  * NetLabel Generic NETLINK Command Definitions
731  */
732
733 static const struct genl_ops netlbl_cipsov4_ops[] = {
734         {
735         .cmd = NLBL_CIPSOV4_C_ADD,
736         .flags = GENL_ADMIN_PERM,
737         .policy = netlbl_cipsov4_genl_policy,
738         .doit = netlbl_cipsov4_add,
739         .dumpit = NULL,
740         },
741         {
742         .cmd = NLBL_CIPSOV4_C_REMOVE,
743         .flags = GENL_ADMIN_PERM,
744         .policy = netlbl_cipsov4_genl_policy,
745         .doit = netlbl_cipsov4_remove,
746         .dumpit = NULL,
747         },
748         {
749         .cmd = NLBL_CIPSOV4_C_LIST,
750         .flags = 0,
751         .policy = netlbl_cipsov4_genl_policy,
752         .doit = netlbl_cipsov4_list,
753         .dumpit = NULL,
754         },
755         {
756         .cmd = NLBL_CIPSOV4_C_LISTALL,
757         .flags = 0,
758         .policy = netlbl_cipsov4_genl_policy,
759         .doit = NULL,
760         .dumpit = netlbl_cipsov4_listall,
761         },
762 };
763
764 static struct genl_family netlbl_cipsov4_gnl_family __ro_after_init = {
765         .hdrsize = 0,
766         .name = NETLBL_NLTYPE_CIPSOV4_NAME,
767         .version = NETLBL_PROTO_VERSION,
768         .maxattr = NLBL_CIPSOV4_A_MAX,
769         .module = THIS_MODULE,
770         .ops = netlbl_cipsov4_ops,
771         .n_ops = ARRAY_SIZE(netlbl_cipsov4_ops),
772 };
773
774 /*
775  * NetLabel Generic NETLINK Protocol Functions
776  */
777
778 /**
779  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
780  *
781  * Description:
782  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
783  * mechanism.  Returns zero on success, negative values on failure.
784  *
785  */
786 int __init netlbl_cipsov4_genl_init(void)
787 {
788         return genl_register_family(&netlbl_cipsov4_gnl_family);
789 }