Merge commit '63cc8c75156462d4b42cbdd76c293b7eee7ddbfe':
[linux-2.6] / net / netlabel / netlabel_cipso_v4.c
1 /*
2  * NetLabel CIPSO/IPv4 Support
3  *
4  * This file defines the CIPSO/IPv4 functions for the NetLabel system.  The
5  * NetLabel system manages static and dynamic label mappings for network
6  * protocols such as CIPSO and RIPSO.
7  *
8  * Author: Paul Moore <paul.moore@hp.com>
9  *
10  */
11
12 /*
13  * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
14  *
15  * This program is free software;  you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
23  * the GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program;  if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28  *
29  */
30
31 #include <linux/types.h>
32 #include <linux/socket.h>
33 #include <linux/string.h>
34 #include <linux/skbuff.h>
35 #include <linux/audit.h>
36 #include <net/sock.h>
37 #include <net/netlink.h>
38 #include <net/genetlink.h>
39 #include <net/netlabel.h>
40 #include <net/cipso_ipv4.h>
41 #include <asm/atomic.h>
42
43 #include "netlabel_user.h"
44 #include "netlabel_cipso_v4.h"
45 #include "netlabel_mgmt.h"
46
47 /* Argument struct for cipso_v4_doi_walk() */
48 struct netlbl_cipsov4_doiwalk_arg {
49         struct netlink_callback *nl_cb;
50         struct sk_buff *skb;
51         u32 seq;
52 };
53
54 /* NetLabel Generic NETLINK CIPSOv4 family */
55 static struct genl_family netlbl_cipsov4_gnl_family = {
56         .id = GENL_ID_GENERATE,
57         .hdrsize = 0,
58         .name = NETLBL_NLTYPE_CIPSOV4_NAME,
59         .version = NETLBL_PROTO_VERSION,
60         .maxattr = NLBL_CIPSOV4_A_MAX,
61 };
62
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_doi_free - Frees a CIPSO V4 DOI definition
85  * @entry: the entry's RCU field
86  *
87  * Description:
88  * This function is designed to be used as a callback to the call_rcu()
89  * function so that the memory allocated to the DOI definition can be released
90  * safely.
91  *
92  */
93 void netlbl_cipsov4_doi_free(struct rcu_head *entry)
94 {
95         struct cipso_v4_doi *ptr;
96
97         ptr = container_of(entry, struct cipso_v4_doi, rcu);
98         switch (ptr->type) {
99         case CIPSO_V4_MAP_STD:
100                 kfree(ptr->map.std->lvl.cipso);
101                 kfree(ptr->map.std->lvl.local);
102                 kfree(ptr->map.std->cat.cipso);
103                 kfree(ptr->map.std->cat.local);
104                 break;
105         }
106         kfree(ptr);
107 }
108
109 /**
110  * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
111  * @info: the Generic NETLINK info block
112  * @doi_def: the CIPSO V4 DOI definition
113  *
114  * Description:
115  * Parse the common sections of a ADD message and fill in the related values
116  * in @doi_def.  Returns zero on success, negative values on failure.
117  *
118  */
119 static int netlbl_cipsov4_add_common(struct genl_info *info,
120                                      struct cipso_v4_doi *doi_def)
121 {
122         struct nlattr *nla;
123         int nla_rem;
124         u32 iter = 0;
125
126         doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
127
128         if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST],
129                                 NLBL_CIPSOV4_A_MAX,
130                                 netlbl_cipsov4_genl_policy) != 0)
131                 return -EINVAL;
132
133         nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem)
134                 if (nla_type(nla) == NLBL_CIPSOV4_A_TAG) {
135                         if (iter >= CIPSO_V4_TAG_MAXCNT)
136                                 return -EINVAL;
137                         doi_def->tags[iter++] = nla_get_u8(nla);
138                 }
139         while (iter < CIPSO_V4_TAG_MAXCNT)
140                 doi_def->tags[iter++] = CIPSO_V4_TAG_INVALID;
141
142         return 0;
143 }
144
145 /*
146  * NetLabel Command Handlers
147  */
148
149 /**
150  * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
151  * @info: the Generic NETLINK info block
152  *
153  * Description:
154  * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message
155  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
156  * error.
157  *
158  */
159 static int netlbl_cipsov4_add_std(struct genl_info *info)
160 {
161         int ret_val = -EINVAL;
162         struct cipso_v4_doi *doi_def = NULL;
163         struct nlattr *nla_a;
164         struct nlattr *nla_b;
165         int nla_a_rem;
166         int nla_b_rem;
167         u32 iter;
168
169         if (!info->attrs[NLBL_CIPSOV4_A_TAGLST] ||
170             !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST])
171                 return -EINVAL;
172
173         if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
174                                 NLBL_CIPSOV4_A_MAX,
175                                 netlbl_cipsov4_genl_policy) != 0)
176                 return -EINVAL;
177
178         doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
179         if (doi_def == NULL)
180                 return -ENOMEM;
181         doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL);
182         if (doi_def->map.std == NULL) {
183                 ret_val = -ENOMEM;
184                 goto add_std_failure;
185         }
186         doi_def->type = CIPSO_V4_MAP_STD;
187
188         ret_val = netlbl_cipsov4_add_common(info, doi_def);
189         if (ret_val != 0)
190                 goto add_std_failure;
191         ret_val = -EINVAL;
192
193         nla_for_each_nested(nla_a,
194                             info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
195                             nla_a_rem)
196                 if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
197                         if (nla_validate_nested(nla_a,
198                                             NLBL_CIPSOV4_A_MAX,
199                                             netlbl_cipsov4_genl_policy) != 0)
200                                         goto add_std_failure;
201                         nla_for_each_nested(nla_b, nla_a, nla_b_rem)
202                                 switch (nla_type(nla_b)) {
203                                 case NLBL_CIPSOV4_A_MLSLVLLOC:
204                                         if (nla_get_u32(nla_b) >
205                                             CIPSO_V4_MAX_LOC_LVLS)
206                                                 goto add_std_failure;
207                                         if (nla_get_u32(nla_b) >=
208                                             doi_def->map.std->lvl.local_size)
209                                              doi_def->map.std->lvl.local_size =
210                                                      nla_get_u32(nla_b) + 1;
211                                         break;
212                                 case NLBL_CIPSOV4_A_MLSLVLREM:
213                                         if (nla_get_u32(nla_b) >
214                                             CIPSO_V4_MAX_REM_LVLS)
215                                                 goto add_std_failure;
216                                         if (nla_get_u32(nla_b) >=
217                                             doi_def->map.std->lvl.cipso_size)
218                                              doi_def->map.std->lvl.cipso_size =
219                                                      nla_get_u32(nla_b) + 1;
220                                         break;
221                                 }
222                 }
223         doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size,
224                                               sizeof(u32),
225                                               GFP_KERNEL);
226         if (doi_def->map.std->lvl.local == NULL) {
227                 ret_val = -ENOMEM;
228                 goto add_std_failure;
229         }
230         doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size,
231                                               sizeof(u32),
232                                               GFP_KERNEL);
233         if (doi_def->map.std->lvl.cipso == NULL) {
234                 ret_val = -ENOMEM;
235                 goto add_std_failure;
236         }
237         for (iter = 0; iter < doi_def->map.std->lvl.local_size; iter++)
238                 doi_def->map.std->lvl.local[iter] = CIPSO_V4_INV_LVL;
239         for (iter = 0; iter < doi_def->map.std->lvl.cipso_size; iter++)
240                 doi_def->map.std->lvl.cipso[iter] = CIPSO_V4_INV_LVL;
241         nla_for_each_nested(nla_a,
242                             info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
243                             nla_a_rem)
244                 if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
245                         struct nlattr *lvl_loc;
246                         struct nlattr *lvl_rem;
247
248                         lvl_loc = nla_find_nested(nla_a,
249                                                   NLBL_CIPSOV4_A_MLSLVLLOC);
250                         lvl_rem = nla_find_nested(nla_a,
251                                                   NLBL_CIPSOV4_A_MLSLVLREM);
252                         if (lvl_loc == NULL || lvl_rem == NULL)
253                                 goto add_std_failure;
254                         doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] =
255                                 nla_get_u32(lvl_rem);
256                         doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] =
257                                 nla_get_u32(lvl_loc);
258                 }
259
260         if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) {
261                 if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
262                                         NLBL_CIPSOV4_A_MAX,
263                                         netlbl_cipsov4_genl_policy) != 0)
264                         goto add_std_failure;
265
266                 nla_for_each_nested(nla_a,
267                                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
268                                     nla_a_rem)
269                         if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
270                                 if (nla_validate_nested(nla_a,
271                                               NLBL_CIPSOV4_A_MAX,
272                                               netlbl_cipsov4_genl_policy) != 0)
273                                         goto add_std_failure;
274                                 nla_for_each_nested(nla_b, nla_a, nla_b_rem)
275                                         switch (nla_type(nla_b)) {
276                                         case NLBL_CIPSOV4_A_MLSCATLOC:
277                                                 if (nla_get_u32(nla_b) >
278                                                     CIPSO_V4_MAX_LOC_CATS)
279                                                         goto add_std_failure;
280                                                 if (nla_get_u32(nla_b) >=
281                                               doi_def->map.std->cat.local_size)
282                                              doi_def->map.std->cat.local_size =
283                                                      nla_get_u32(nla_b) + 1;
284                                                 break;
285                                         case NLBL_CIPSOV4_A_MLSCATREM:
286                                                 if (nla_get_u32(nla_b) >
287                                                     CIPSO_V4_MAX_REM_CATS)
288                                                         goto add_std_failure;
289                                                 if (nla_get_u32(nla_b) >=
290                                               doi_def->map.std->cat.cipso_size)
291                                              doi_def->map.std->cat.cipso_size =
292                                                      nla_get_u32(nla_b) + 1;
293                                                 break;
294                                         }
295                         }
296                 doi_def->map.std->cat.local = kcalloc(
297                                               doi_def->map.std->cat.local_size,
298                                               sizeof(u32),
299                                               GFP_KERNEL);
300                 if (doi_def->map.std->cat.local == NULL) {
301                         ret_val = -ENOMEM;
302                         goto add_std_failure;
303                 }
304                 doi_def->map.std->cat.cipso = kcalloc(
305                                               doi_def->map.std->cat.cipso_size,
306                                               sizeof(u32),
307                                               GFP_KERNEL);
308                 if (doi_def->map.std->cat.cipso == NULL) {
309                         ret_val = -ENOMEM;
310                         goto add_std_failure;
311                 }
312                 for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++)
313                         doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT;
314                 for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++)
315                         doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT;
316                 nla_for_each_nested(nla_a,
317                                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
318                                     nla_a_rem)
319                         if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
320                                 struct nlattr *cat_loc;
321                                 struct nlattr *cat_rem;
322
323                                 cat_loc = nla_find_nested(nla_a,
324                                                      NLBL_CIPSOV4_A_MLSCATLOC);
325                                 cat_rem = nla_find_nested(nla_a,
326                                                      NLBL_CIPSOV4_A_MLSCATREM);
327                                 if (cat_loc == NULL || cat_rem == NULL)
328                                         goto add_std_failure;
329                                 doi_def->map.std->cat.local[
330                                                         nla_get_u32(cat_loc)] =
331                                         nla_get_u32(cat_rem);
332                                 doi_def->map.std->cat.cipso[
333                                                         nla_get_u32(cat_rem)] =
334                                         nla_get_u32(cat_loc);
335                         }
336         }
337
338         ret_val = cipso_v4_doi_add(doi_def);
339         if (ret_val != 0)
340                 goto add_std_failure;
341         return 0;
342
343 add_std_failure:
344         if (doi_def)
345                 netlbl_cipsov4_doi_free(&doi_def->rcu);
346         return ret_val;
347 }
348
349 /**
350  * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
351  * @info: the Generic NETLINK info block
352  *
353  * Description:
354  * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
355  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
356  * error.
357  *
358  */
359 static int netlbl_cipsov4_add_pass(struct genl_info *info)
360 {
361         int ret_val;
362         struct cipso_v4_doi *doi_def = NULL;
363
364         if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
365                 return -EINVAL;
366
367         doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
368         if (doi_def == NULL)
369                 return -ENOMEM;
370         doi_def->type = CIPSO_V4_MAP_PASS;
371
372         ret_val = netlbl_cipsov4_add_common(info, doi_def);
373         if (ret_val != 0)
374                 goto add_pass_failure;
375
376         ret_val = cipso_v4_doi_add(doi_def);
377         if (ret_val != 0)
378                 goto add_pass_failure;
379         return 0;
380
381 add_pass_failure:
382         netlbl_cipsov4_doi_free(&doi_def->rcu);
383         return ret_val;
384 }
385
386 /**
387  * netlbl_cipsov4_add - Handle an ADD message
388  * @skb: the NETLINK buffer
389  * @info: the Generic NETLINK info block
390  *
391  * Description:
392  * Create a new DOI definition based on the given ADD message and add it to the
393  * CIPSO V4 engine.  Returns zero on success, negative values on failure.
394  *
395  */
396 static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
397
398 {
399         int ret_val = -EINVAL;
400         u32 type;
401         u32 doi;
402         const char *type_str = "(unknown)";
403         struct audit_buffer *audit_buf;
404         struct netlbl_audit audit_info;
405
406         if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
407             !info->attrs[NLBL_CIPSOV4_A_MTYPE])
408                 return -EINVAL;
409
410         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
411         netlbl_netlink_auditinfo(skb, &audit_info);
412
413         type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
414         switch (type) {
415         case CIPSO_V4_MAP_STD:
416                 type_str = "std";
417                 ret_val = netlbl_cipsov4_add_std(info);
418                 break;
419         case CIPSO_V4_MAP_PASS:
420                 type_str = "pass";
421                 ret_val = netlbl_cipsov4_add_pass(info);
422                 break;
423         }
424         if (ret_val == 0)
425                 atomic_inc(&netlabel_mgmt_protocount);
426
427         audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
428                                               &audit_info);
429         if (audit_buf != NULL) {
430                 audit_log_format(audit_buf,
431                                  " cipso_doi=%u cipso_type=%s res=%u",
432                                  doi,
433                                  type_str,
434                                  ret_val == 0 ? 1 : 0);
435                 audit_log_end(audit_buf);
436         }
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;
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_STD:
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).pid,
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         return genlmsg_end(cb_arg->skb, data);
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         int 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 - Handle a REMOVE message
672  * @skb: the NETLINK buffer
673  * @info: the Generic NETLINK info block
674  *
675  * Description:
676  * Process a user generated REMOVE message and respond accordingly.  Returns
677  * zero on success, negative values on failure.
678  *
679  */
680 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
681 {
682         int ret_val = -EINVAL;
683         u32 doi = 0;
684         struct audit_buffer *audit_buf;
685         struct netlbl_audit audit_info;
686
687         if (!info->attrs[NLBL_CIPSOV4_A_DOI])
688                 return -EINVAL;
689
690         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
691         netlbl_netlink_auditinfo(skb, &audit_info);
692
693         ret_val = cipso_v4_doi_remove(doi,
694                                       &audit_info,
695                                       netlbl_cipsov4_doi_free);
696         if (ret_val == 0)
697                 atomic_dec(&netlabel_mgmt_protocount);
698
699         audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
700                                               &audit_info);
701         if (audit_buf != NULL) {
702                 audit_log_format(audit_buf,
703                                  " cipso_doi=%u res=%u",
704                                  doi,
705                                  ret_val == 0 ? 1 : 0);
706                 audit_log_end(audit_buf);
707         }
708
709         return ret_val;
710 }
711
712 /*
713  * NetLabel Generic NETLINK Command Definitions
714  */
715
716 static struct genl_ops netlbl_cipsov4_ops[] = {
717         {
718         .cmd = NLBL_CIPSOV4_C_ADD,
719         .flags = GENL_ADMIN_PERM,
720         .policy = netlbl_cipsov4_genl_policy,
721         .doit = netlbl_cipsov4_add,
722         .dumpit = NULL,
723         },
724         {
725         .cmd = NLBL_CIPSOV4_C_REMOVE,
726         .flags = GENL_ADMIN_PERM,
727         .policy = netlbl_cipsov4_genl_policy,
728         .doit = netlbl_cipsov4_remove,
729         .dumpit = NULL,
730         },
731         {
732         .cmd = NLBL_CIPSOV4_C_LIST,
733         .flags = 0,
734         .policy = netlbl_cipsov4_genl_policy,
735         .doit = netlbl_cipsov4_list,
736         .dumpit = NULL,
737         },
738         {
739         .cmd = NLBL_CIPSOV4_C_LISTALL,
740         .flags = 0,
741         .policy = netlbl_cipsov4_genl_policy,
742         .doit = NULL,
743         .dumpit = netlbl_cipsov4_listall,
744         },
745 };
746
747 /*
748  * NetLabel Generic NETLINK Protocol Functions
749  */
750
751 /**
752  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
753  *
754  * Description:
755  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
756  * mechanism.  Returns zero on success, negative values on failure.
757  *
758  */
759 int __init netlbl_cipsov4_genl_init(void)
760 {
761         int ret_val, i;
762
763         ret_val = genl_register_family(&netlbl_cipsov4_gnl_family);
764         if (ret_val != 0)
765                 return ret_val;
766
767         for (i = 0; i < ARRAY_SIZE(netlbl_cipsov4_ops); i++) {
768                 ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
769                                 &netlbl_cipsov4_ops[i]);
770                 if (ret_val != 0)
771                         return ret_val;
772         }
773
774         return 0;
775 }