Merge branch 'x86/nmi' into x86/devel
[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
588         ret_val = genlmsg_reply(ans_skb, info);
589         if (ret_val != 0)
590                 goto list_failure;
591
592         return 0;
593
594 list_retry:
595         /* XXX - this limit is a guesstimate */
596         if (nlsze_mult < 4) {
597                 rcu_read_unlock();
598                 kfree_skb(ans_skb);
599                 nlsze_mult++;
600                 goto list_start;
601         }
602 list_failure_lock:
603         rcu_read_unlock();
604 list_failure:
605         kfree_skb(ans_skb);
606         return ret_val;
607 }
608
609 /**
610  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
611  * @doi_def: the CIPSOv4 DOI definition
612  * @arg: the netlbl_cipsov4_doiwalk_arg structure
613  *
614  * Description:
615  * This function is designed to be used as a callback to the
616  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
617  * message.  Returns the size of the message on success, negative values on
618  * failure.
619  *
620  */
621 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
622 {
623         int ret_val = -ENOMEM;
624         struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
625         void *data;
626
627         data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid,
628                            cb_arg->seq, &netlbl_cipsov4_gnl_family,
629                            NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
630         if (data == NULL)
631                 goto listall_cb_failure;
632
633         ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
634         if (ret_val != 0)
635                 goto listall_cb_failure;
636         ret_val = nla_put_u32(cb_arg->skb,
637                               NLBL_CIPSOV4_A_MTYPE,
638                               doi_def->type);
639         if (ret_val != 0)
640                 goto listall_cb_failure;
641
642         return genlmsg_end(cb_arg->skb, data);
643
644 listall_cb_failure:
645         genlmsg_cancel(cb_arg->skb, data);
646         return ret_val;
647 }
648
649 /**
650  * netlbl_cipsov4_listall - Handle a LISTALL message
651  * @skb: the NETLINK buffer
652  * @cb: the NETLINK callback
653  *
654  * Description:
655  * Process a user generated LISTALL message and respond accordingly.  Returns
656  * zero on success and negative values on error.
657  *
658  */
659 static int netlbl_cipsov4_listall(struct sk_buff *skb,
660                                   struct netlink_callback *cb)
661 {
662         struct netlbl_cipsov4_doiwalk_arg cb_arg;
663         int doi_skip = cb->args[0];
664
665         cb_arg.nl_cb = cb;
666         cb_arg.skb = skb;
667         cb_arg.seq = cb->nlh->nlmsg_seq;
668
669         cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
670
671         cb->args[0] = doi_skip;
672         return skb->len;
673 }
674
675 /**
676  * netlbl_cipsov4_remove - Handle a REMOVE message
677  * @skb: the NETLINK buffer
678  * @info: the Generic NETLINK info block
679  *
680  * Description:
681  * Process a user generated REMOVE message and respond accordingly.  Returns
682  * zero on success, negative values on failure.
683  *
684  */
685 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
686 {
687         int ret_val = -EINVAL;
688         u32 doi = 0;
689         struct audit_buffer *audit_buf;
690         struct netlbl_audit audit_info;
691
692         if (!info->attrs[NLBL_CIPSOV4_A_DOI])
693                 return -EINVAL;
694
695         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
696         netlbl_netlink_auditinfo(skb, &audit_info);
697
698         ret_val = cipso_v4_doi_remove(doi,
699                                       &audit_info,
700                                       netlbl_cipsov4_doi_free);
701         if (ret_val == 0)
702                 atomic_dec(&netlabel_mgmt_protocount);
703
704         audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
705                                               &audit_info);
706         if (audit_buf != NULL) {
707                 audit_log_format(audit_buf,
708                                  " cipso_doi=%u res=%u",
709                                  doi,
710                                  ret_val == 0 ? 1 : 0);
711                 audit_log_end(audit_buf);
712         }
713
714         return ret_val;
715 }
716
717 /*
718  * NetLabel Generic NETLINK Command Definitions
719  */
720
721 static struct genl_ops netlbl_cipsov4_ops[] = {
722         {
723         .cmd = NLBL_CIPSOV4_C_ADD,
724         .flags = GENL_ADMIN_PERM,
725         .policy = netlbl_cipsov4_genl_policy,
726         .doit = netlbl_cipsov4_add,
727         .dumpit = NULL,
728         },
729         {
730         .cmd = NLBL_CIPSOV4_C_REMOVE,
731         .flags = GENL_ADMIN_PERM,
732         .policy = netlbl_cipsov4_genl_policy,
733         .doit = netlbl_cipsov4_remove,
734         .dumpit = NULL,
735         },
736         {
737         .cmd = NLBL_CIPSOV4_C_LIST,
738         .flags = 0,
739         .policy = netlbl_cipsov4_genl_policy,
740         .doit = netlbl_cipsov4_list,
741         .dumpit = NULL,
742         },
743         {
744         .cmd = NLBL_CIPSOV4_C_LISTALL,
745         .flags = 0,
746         .policy = netlbl_cipsov4_genl_policy,
747         .doit = NULL,
748         .dumpit = netlbl_cipsov4_listall,
749         },
750 };
751
752 /*
753  * NetLabel Generic NETLINK Protocol Functions
754  */
755
756 /**
757  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
758  *
759  * Description:
760  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
761  * mechanism.  Returns zero on success, negative values on failure.
762  *
763  */
764 int __init netlbl_cipsov4_genl_init(void)
765 {
766         int ret_val, i;
767
768         ret_val = genl_register_family(&netlbl_cipsov4_gnl_family);
769         if (ret_val != 0)
770                 return ret_val;
771
772         for (i = 0; i < ARRAY_SIZE(netlbl_cipsov4_ops); i++) {
773                 ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
774                                 &netlbl_cipsov4_ops[i]);
775                 if (ret_val != 0)
776                         return ret_val;
777         }
778
779         return 0;
780 }