Merge tag 'clk-for-linus-3.12' of git://git.linaro.org/people/mturquette/linux
[linux-drm-fsl-dcu.git] / net / netlabel / netlabel_mgmt.c
1 /*
2  * NetLabel Management Support
3  *
4  * This file defines the management 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, 2008
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/in.h>
36 #include <linux/in6.h>
37 #include <linux/slab.h>
38 #include <net/sock.h>
39 #include <net/netlink.h>
40 #include <net/genetlink.h>
41 #include <net/ip.h>
42 #include <net/ipv6.h>
43 #include <net/netlabel.h>
44 #include <net/cipso_ipv4.h>
45 #include <linux/atomic.h>
46
47 #include "netlabel_domainhash.h"
48 #include "netlabel_user.h"
49 #include "netlabel_mgmt.h"
50
51 /* NetLabel configured protocol counter */
52 atomic_t netlabel_mgmt_protocount = ATOMIC_INIT(0);
53
54 /* Argument struct for netlbl_domhsh_walk() */
55 struct netlbl_domhsh_walk_arg {
56         struct netlink_callback *nl_cb;
57         struct sk_buff *skb;
58         u32 seq;
59 };
60
61 /* NetLabel Generic NETLINK CIPSOv4 family */
62 static struct genl_family netlbl_mgmt_gnl_family = {
63         .id = GENL_ID_GENERATE,
64         .hdrsize = 0,
65         .name = NETLBL_NLTYPE_MGMT_NAME,
66         .version = NETLBL_PROTO_VERSION,
67         .maxattr = NLBL_MGMT_A_MAX,
68 };
69
70 /* NetLabel Netlink attribute policy */
71 static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
72         [NLBL_MGMT_A_DOMAIN] = { .type = NLA_NUL_STRING },
73         [NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 },
74         [NLBL_MGMT_A_VERSION] = { .type = NLA_U32 },
75         [NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 },
76 };
77
78 /*
79  * Helper Functions
80  */
81
82 /**
83  * netlbl_mgmt_add - Handle an ADD message
84  * @info: the Generic NETLINK info block
85  * @audit_info: NetLabel audit information
86  *
87  * Description:
88  * Helper function for the ADD and ADDDEF messages to add the domain mappings
89  * from the message to the hash table.  See netlabel.h for a description of the
90  * message format.  Returns zero on success, negative values on failure.
91  *
92  */
93 static int netlbl_mgmt_add_common(struct genl_info *info,
94                                   struct netlbl_audit *audit_info)
95 {
96         int ret_val = -EINVAL;
97         struct netlbl_dom_map *entry = NULL;
98         struct netlbl_domaddr_map *addrmap = NULL;
99         struct cipso_v4_doi *cipsov4 = NULL;
100         u32 tmp_val;
101
102         entry = kzalloc(sizeof(*entry), GFP_KERNEL);
103         if (entry == NULL) {
104                 ret_val = -ENOMEM;
105                 goto add_failure;
106         }
107         entry->def.type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
108         if (info->attrs[NLBL_MGMT_A_DOMAIN]) {
109                 size_t tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
110                 entry->domain = kmalloc(tmp_size, GFP_KERNEL);
111                 if (entry->domain == NULL) {
112                         ret_val = -ENOMEM;
113                         goto add_failure;
114                 }
115                 nla_strlcpy(entry->domain,
116                             info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
117         }
118
119         /* NOTE: internally we allow/use a entry->def.type value of
120          *       NETLBL_NLTYPE_ADDRSELECT but we don't currently allow users
121          *       to pass that as a protocol value because we need to know the
122          *       "real" protocol */
123
124         switch (entry->def.type) {
125         case NETLBL_NLTYPE_UNLABELED:
126                 break;
127         case NETLBL_NLTYPE_CIPSOV4:
128                 if (!info->attrs[NLBL_MGMT_A_CV4DOI])
129                         goto add_failure;
130
131                 tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
132                 cipsov4 = cipso_v4_doi_getdef(tmp_val);
133                 if (cipsov4 == NULL)
134                         goto add_failure;
135                 entry->def.cipso = cipsov4;
136                 break;
137         default:
138                 goto add_failure;
139         }
140
141         if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) {
142                 struct in_addr *addr;
143                 struct in_addr *mask;
144                 struct netlbl_domaddr4_map *map;
145
146                 addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
147                 if (addrmap == NULL) {
148                         ret_val = -ENOMEM;
149                         goto add_failure;
150                 }
151                 INIT_LIST_HEAD(&addrmap->list4);
152                 INIT_LIST_HEAD(&addrmap->list6);
153
154                 if (nla_len(info->attrs[NLBL_MGMT_A_IPV4ADDR]) !=
155                     sizeof(struct in_addr)) {
156                         ret_val = -EINVAL;
157                         goto add_failure;
158                 }
159                 if (nla_len(info->attrs[NLBL_MGMT_A_IPV4MASK]) !=
160                     sizeof(struct in_addr)) {
161                         ret_val = -EINVAL;
162                         goto add_failure;
163                 }
164                 addr = nla_data(info->attrs[NLBL_MGMT_A_IPV4ADDR]);
165                 mask = nla_data(info->attrs[NLBL_MGMT_A_IPV4MASK]);
166
167                 map = kzalloc(sizeof(*map), GFP_KERNEL);
168                 if (map == NULL) {
169                         ret_val = -ENOMEM;
170                         goto add_failure;
171                 }
172                 map->list.addr = addr->s_addr & mask->s_addr;
173                 map->list.mask = mask->s_addr;
174                 map->list.valid = 1;
175                 map->def.type = entry->def.type;
176                 if (cipsov4)
177                         map->def.cipso = cipsov4;
178
179                 ret_val = netlbl_af4list_add(&map->list, &addrmap->list4);
180                 if (ret_val != 0) {
181                         kfree(map);
182                         goto add_failure;
183                 }
184
185                 entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
186                 entry->def.addrsel = addrmap;
187 #if IS_ENABLED(CONFIG_IPV6)
188         } else if (info->attrs[NLBL_MGMT_A_IPV6ADDR]) {
189                 struct in6_addr *addr;
190                 struct in6_addr *mask;
191                 struct netlbl_domaddr6_map *map;
192
193                 addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
194                 if (addrmap == NULL) {
195                         ret_val = -ENOMEM;
196                         goto add_failure;
197                 }
198                 INIT_LIST_HEAD(&addrmap->list4);
199                 INIT_LIST_HEAD(&addrmap->list6);
200
201                 if (nla_len(info->attrs[NLBL_MGMT_A_IPV6ADDR]) !=
202                     sizeof(struct in6_addr)) {
203                         ret_val = -EINVAL;
204                         goto add_failure;
205                 }
206                 if (nla_len(info->attrs[NLBL_MGMT_A_IPV6MASK]) !=
207                     sizeof(struct in6_addr)) {
208                         ret_val = -EINVAL;
209                         goto add_failure;
210                 }
211                 addr = nla_data(info->attrs[NLBL_MGMT_A_IPV6ADDR]);
212                 mask = nla_data(info->attrs[NLBL_MGMT_A_IPV6MASK]);
213
214                 map = kzalloc(sizeof(*map), GFP_KERNEL);
215                 if (map == NULL) {
216                         ret_val = -ENOMEM;
217                         goto add_failure;
218                 }
219                 map->list.addr = *addr;
220                 map->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
221                 map->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
222                 map->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
223                 map->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
224                 map->list.mask = *mask;
225                 map->list.valid = 1;
226                 map->def.type = entry->def.type;
227
228                 ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
229                 if (ret_val != 0) {
230                         kfree(map);
231                         goto add_failure;
232                 }
233
234                 entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
235                 entry->def.addrsel = addrmap;
236 #endif /* IPv6 */
237         }
238
239         ret_val = netlbl_domhsh_add(entry, audit_info);
240         if (ret_val != 0)
241                 goto add_failure;
242
243         return 0;
244
245 add_failure:
246         if (cipsov4)
247                 cipso_v4_doi_putdef(cipsov4);
248         if (entry)
249                 kfree(entry->domain);
250         kfree(addrmap);
251         kfree(entry);
252         return ret_val;
253 }
254
255 /**
256  * netlbl_mgmt_listentry - List a NetLabel/LSM domain map entry
257  * @skb: the NETLINK buffer
258  * @entry: the map entry
259  *
260  * Description:
261  * This function is a helper function used by the LISTALL and LISTDEF command
262  * handlers.  The caller is responsible for ensuring that the RCU read lock
263  * is held.  Returns zero on success, negative values on failure.
264  *
265  */
266 static int netlbl_mgmt_listentry(struct sk_buff *skb,
267                                  struct netlbl_dom_map *entry)
268 {
269         int ret_val = 0;
270         struct nlattr *nla_a;
271         struct nlattr *nla_b;
272         struct netlbl_af4list *iter4;
273 #if IS_ENABLED(CONFIG_IPV6)
274         struct netlbl_af6list *iter6;
275 #endif
276
277         if (entry->domain != NULL) {
278                 ret_val = nla_put_string(skb,
279                                          NLBL_MGMT_A_DOMAIN, entry->domain);
280                 if (ret_val != 0)
281                         return ret_val;
282         }
283
284         switch (entry->def.type) {
285         case NETLBL_NLTYPE_ADDRSELECT:
286                 nla_a = nla_nest_start(skb, NLBL_MGMT_A_SELECTORLIST);
287                 if (nla_a == NULL)
288                         return -ENOMEM;
289
290                 netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4) {
291                         struct netlbl_domaddr4_map *map4;
292                         struct in_addr addr_struct;
293
294                         nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
295                         if (nla_b == NULL)
296                                 return -ENOMEM;
297
298                         addr_struct.s_addr = iter4->addr;
299                         ret_val = nla_put(skb, NLBL_MGMT_A_IPV4ADDR,
300                                           sizeof(struct in_addr),
301                                           &addr_struct);
302                         if (ret_val != 0)
303                                 return ret_val;
304                         addr_struct.s_addr = iter4->mask;
305                         ret_val = nla_put(skb, NLBL_MGMT_A_IPV4MASK,
306                                           sizeof(struct in_addr),
307                                           &addr_struct);
308                         if (ret_val != 0)
309                                 return ret_val;
310                         map4 = netlbl_domhsh_addr4_entry(iter4);
311                         ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
312                                               map4->def.type);
313                         if (ret_val != 0)
314                                 return ret_val;
315                         switch (map4->def.type) {
316                         case NETLBL_NLTYPE_CIPSOV4:
317                                 ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
318                                                       map4->def.cipso->doi);
319                                 if (ret_val != 0)
320                                         return ret_val;
321                                 break;
322                         }
323
324                         nla_nest_end(skb, nla_b);
325                 }
326 #if IS_ENABLED(CONFIG_IPV6)
327                 netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6) {
328                         struct netlbl_domaddr6_map *map6;
329
330                         nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
331                         if (nla_b == NULL)
332                                 return -ENOMEM;
333
334                         ret_val = nla_put(skb, NLBL_MGMT_A_IPV6ADDR,
335                                           sizeof(struct in6_addr),
336                                           &iter6->addr);
337                         if (ret_val != 0)
338                                 return ret_val;
339                         ret_val = nla_put(skb, NLBL_MGMT_A_IPV6MASK,
340                                           sizeof(struct in6_addr),
341                                           &iter6->mask);
342                         if (ret_val != 0)
343                                 return ret_val;
344                         map6 = netlbl_domhsh_addr6_entry(iter6);
345                         ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
346                                               map6->def.type);
347                         if (ret_val != 0)
348                                 return ret_val;
349
350                         nla_nest_end(skb, nla_b);
351                 }
352 #endif /* IPv6 */
353
354                 nla_nest_end(skb, nla_a);
355                 break;
356         case NETLBL_NLTYPE_UNLABELED:
357                 ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type);
358                 break;
359         case NETLBL_NLTYPE_CIPSOV4:
360                 ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type);
361                 if (ret_val != 0)
362                         return ret_val;
363                 ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
364                                       entry->def.cipso->doi);
365                 break;
366         }
367
368         return ret_val;
369 }
370
371 /*
372  * NetLabel Command Handlers
373  */
374
375 /**
376  * netlbl_mgmt_add - Handle an ADD message
377  * @skb: the NETLINK buffer
378  * @info: the Generic NETLINK info block
379  *
380  * Description:
381  * Process a user generated ADD message and add the domains from the message
382  * to the hash table.  See netlabel.h for a description of the message format.
383  * Returns zero on success, negative values on failure.
384  *
385  */
386 static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
387 {
388         struct netlbl_audit audit_info;
389
390         if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) ||
391             (!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
392             (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
393              info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
394             (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
395              info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
396             ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
397              (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
398             ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
399              (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
400                 return -EINVAL;
401
402         netlbl_netlink_auditinfo(skb, &audit_info);
403
404         return netlbl_mgmt_add_common(info, &audit_info);
405 }
406
407 /**
408  * netlbl_mgmt_remove - Handle a REMOVE message
409  * @skb: the NETLINK buffer
410  * @info: the Generic NETLINK info block
411  *
412  * Description:
413  * Process a user generated REMOVE message and remove the specified domain
414  * mappings.  Returns zero on success, negative values on failure.
415  *
416  */
417 static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info)
418 {
419         char *domain;
420         struct netlbl_audit audit_info;
421
422         if (!info->attrs[NLBL_MGMT_A_DOMAIN])
423                 return -EINVAL;
424
425         netlbl_netlink_auditinfo(skb, &audit_info);
426
427         domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]);
428         return netlbl_domhsh_remove(domain, &audit_info);
429 }
430
431 /**
432  * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL
433  * @entry: the domain mapping hash table entry
434  * @arg: the netlbl_domhsh_walk_arg structure
435  *
436  * Description:
437  * This function is designed to be used as a callback to the
438  * netlbl_domhsh_walk() function for use in generating a response for a LISTALL
439  * message.  Returns the size of the message on success, negative values on
440  * failure.
441  *
442  */
443 static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg)
444 {
445         int ret_val = -ENOMEM;
446         struct netlbl_domhsh_walk_arg *cb_arg = arg;
447         void *data;
448
449         data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
450                            cb_arg->seq, &netlbl_mgmt_gnl_family,
451                            NLM_F_MULTI, NLBL_MGMT_C_LISTALL);
452         if (data == NULL)
453                 goto listall_cb_failure;
454
455         ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry);
456         if (ret_val != 0)
457                 goto listall_cb_failure;
458
459         cb_arg->seq++;
460         return genlmsg_end(cb_arg->skb, data);
461
462 listall_cb_failure:
463         genlmsg_cancel(cb_arg->skb, data);
464         return ret_val;
465 }
466
467 /**
468  * netlbl_mgmt_listall - Handle a LISTALL message
469  * @skb: the NETLINK buffer
470  * @cb: the NETLINK callback
471  *
472  * Description:
473  * Process a user generated LISTALL message and dumps the domain hash table in
474  * a form suitable for use in a kernel generated LISTALL message.  Returns zero
475  * on success, negative values on failure.
476  *
477  */
478 static int netlbl_mgmt_listall(struct sk_buff *skb,
479                                struct netlink_callback *cb)
480 {
481         struct netlbl_domhsh_walk_arg cb_arg;
482         u32 skip_bkt = cb->args[0];
483         u32 skip_chain = cb->args[1];
484
485         cb_arg.nl_cb = cb;
486         cb_arg.skb = skb;
487         cb_arg.seq = cb->nlh->nlmsg_seq;
488
489         netlbl_domhsh_walk(&skip_bkt,
490                            &skip_chain,
491                            netlbl_mgmt_listall_cb,
492                            &cb_arg);
493
494         cb->args[0] = skip_bkt;
495         cb->args[1] = skip_chain;
496         return skb->len;
497 }
498
499 /**
500  * netlbl_mgmt_adddef - Handle an ADDDEF message
501  * @skb: the NETLINK buffer
502  * @info: the Generic NETLINK info block
503  *
504  * Description:
505  * Process a user generated ADDDEF message and respond accordingly.  Returns
506  * zero on success, negative values on failure.
507  *
508  */
509 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
510 {
511         struct netlbl_audit audit_info;
512
513         if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
514             (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
515              info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
516             (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
517              info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
518             ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
519              (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
520             ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
521              (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
522                 return -EINVAL;
523
524         netlbl_netlink_auditinfo(skb, &audit_info);
525
526         return netlbl_mgmt_add_common(info, &audit_info);
527 }
528
529 /**
530  * netlbl_mgmt_removedef - Handle a REMOVEDEF message
531  * @skb: the NETLINK buffer
532  * @info: the Generic NETLINK info block
533  *
534  * Description:
535  * Process a user generated REMOVEDEF message and remove the default domain
536  * mapping.  Returns zero on success, negative values on failure.
537  *
538  */
539 static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info)
540 {
541         struct netlbl_audit audit_info;
542
543         netlbl_netlink_auditinfo(skb, &audit_info);
544
545         return netlbl_domhsh_remove_default(&audit_info);
546 }
547
548 /**
549  * netlbl_mgmt_listdef - Handle a LISTDEF message
550  * @skb: the NETLINK buffer
551  * @info: the Generic NETLINK info block
552  *
553  * Description:
554  * Process a user generated LISTDEF message and dumps the default domain
555  * mapping in a form suitable for use in a kernel generated LISTDEF message.
556  * Returns zero on success, negative values on failure.
557  *
558  */
559 static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
560 {
561         int ret_val = -ENOMEM;
562         struct sk_buff *ans_skb = NULL;
563         void *data;
564         struct netlbl_dom_map *entry;
565
566         ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
567         if (ans_skb == NULL)
568                 return -ENOMEM;
569         data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family,
570                                  0, NLBL_MGMT_C_LISTDEF);
571         if (data == NULL)
572                 goto listdef_failure;
573
574         rcu_read_lock();
575         entry = netlbl_domhsh_getentry(NULL);
576         if (entry == NULL) {
577                 ret_val = -ENOENT;
578                 goto listdef_failure_lock;
579         }
580         ret_val = netlbl_mgmt_listentry(ans_skb, entry);
581         rcu_read_unlock();
582         if (ret_val != 0)
583                 goto listdef_failure;
584
585         genlmsg_end(ans_skb, data);
586         return genlmsg_reply(ans_skb, info);
587
588 listdef_failure_lock:
589         rcu_read_unlock();
590 listdef_failure:
591         kfree_skb(ans_skb);
592         return ret_val;
593 }
594
595 /**
596  * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response
597  * @skb: the skb to write to
598  * @cb: the NETLINK callback
599  * @protocol: the NetLabel protocol to use in the message
600  *
601  * Description:
602  * This function is to be used in conjunction with netlbl_mgmt_protocols() to
603  * answer a application's PROTOCOLS message.  Returns the size of the message
604  * on success, negative values on failure.
605  *
606  */
607 static int netlbl_mgmt_protocols_cb(struct sk_buff *skb,
608                                     struct netlink_callback *cb,
609                                     u32 protocol)
610 {
611         int ret_val = -ENOMEM;
612         void *data;
613
614         data = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
615                            &netlbl_mgmt_gnl_family, NLM_F_MULTI,
616                            NLBL_MGMT_C_PROTOCOLS);
617         if (data == NULL)
618                 goto protocols_cb_failure;
619
620         ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol);
621         if (ret_val != 0)
622                 goto protocols_cb_failure;
623
624         return genlmsg_end(skb, data);
625
626 protocols_cb_failure:
627         genlmsg_cancel(skb, data);
628         return ret_val;
629 }
630
631 /**
632  * netlbl_mgmt_protocols - Handle a PROTOCOLS message
633  * @skb: the NETLINK buffer
634  * @cb: the NETLINK callback
635  *
636  * Description:
637  * Process a user generated PROTOCOLS message and respond accordingly.
638  *
639  */
640 static int netlbl_mgmt_protocols(struct sk_buff *skb,
641                                  struct netlink_callback *cb)
642 {
643         u32 protos_sent = cb->args[0];
644
645         if (protos_sent == 0) {
646                 if (netlbl_mgmt_protocols_cb(skb,
647                                              cb,
648                                              NETLBL_NLTYPE_UNLABELED) < 0)
649                         goto protocols_return;
650                 protos_sent++;
651         }
652         if (protos_sent == 1) {
653                 if (netlbl_mgmt_protocols_cb(skb,
654                                              cb,
655                                              NETLBL_NLTYPE_CIPSOV4) < 0)
656                         goto protocols_return;
657                 protos_sent++;
658         }
659
660 protocols_return:
661         cb->args[0] = protos_sent;
662         return skb->len;
663 }
664
665 /**
666  * netlbl_mgmt_version - Handle a VERSION message
667  * @skb: the NETLINK buffer
668  * @info: the Generic NETLINK info block
669  *
670  * Description:
671  * Process a user generated VERSION message and respond accordingly.  Returns
672  * zero on success, negative values on failure.
673  *
674  */
675 static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info)
676 {
677         int ret_val = -ENOMEM;
678         struct sk_buff *ans_skb = NULL;
679         void *data;
680
681         ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
682         if (ans_skb == NULL)
683                 return -ENOMEM;
684         data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family,
685                                  0, NLBL_MGMT_C_VERSION);
686         if (data == NULL)
687                 goto version_failure;
688
689         ret_val = nla_put_u32(ans_skb,
690                               NLBL_MGMT_A_VERSION,
691                               NETLBL_PROTO_VERSION);
692         if (ret_val != 0)
693                 goto version_failure;
694
695         genlmsg_end(ans_skb, data);
696         return genlmsg_reply(ans_skb, info);
697
698 version_failure:
699         kfree_skb(ans_skb);
700         return ret_val;
701 }
702
703
704 /*
705  * NetLabel Generic NETLINK Command Definitions
706  */
707
708 static struct genl_ops netlbl_mgmt_genl_ops[] = {
709         {
710         .cmd = NLBL_MGMT_C_ADD,
711         .flags = GENL_ADMIN_PERM,
712         .policy = netlbl_mgmt_genl_policy,
713         .doit = netlbl_mgmt_add,
714         .dumpit = NULL,
715         },
716         {
717         .cmd = NLBL_MGMT_C_REMOVE,
718         .flags = GENL_ADMIN_PERM,
719         .policy = netlbl_mgmt_genl_policy,
720         .doit = netlbl_mgmt_remove,
721         .dumpit = NULL,
722         },
723         {
724         .cmd = NLBL_MGMT_C_LISTALL,
725         .flags = 0,
726         .policy = netlbl_mgmt_genl_policy,
727         .doit = NULL,
728         .dumpit = netlbl_mgmt_listall,
729         },
730         {
731         .cmd = NLBL_MGMT_C_ADDDEF,
732         .flags = GENL_ADMIN_PERM,
733         .policy = netlbl_mgmt_genl_policy,
734         .doit = netlbl_mgmt_adddef,
735         .dumpit = NULL,
736         },
737         {
738         .cmd = NLBL_MGMT_C_REMOVEDEF,
739         .flags = GENL_ADMIN_PERM,
740         .policy = netlbl_mgmt_genl_policy,
741         .doit = netlbl_mgmt_removedef,
742         .dumpit = NULL,
743         },
744         {
745         .cmd = NLBL_MGMT_C_LISTDEF,
746         .flags = 0,
747         .policy = netlbl_mgmt_genl_policy,
748         .doit = netlbl_mgmt_listdef,
749         .dumpit = NULL,
750         },
751         {
752         .cmd = NLBL_MGMT_C_PROTOCOLS,
753         .flags = 0,
754         .policy = netlbl_mgmt_genl_policy,
755         .doit = NULL,
756         .dumpit = netlbl_mgmt_protocols,
757         },
758         {
759         .cmd = NLBL_MGMT_C_VERSION,
760         .flags = 0,
761         .policy = netlbl_mgmt_genl_policy,
762         .doit = netlbl_mgmt_version,
763         .dumpit = NULL,
764         },
765 };
766
767 /*
768  * NetLabel Generic NETLINK Protocol Functions
769  */
770
771 /**
772  * netlbl_mgmt_genl_init - Register the NetLabel management component
773  *
774  * Description:
775  * Register the NetLabel management component with the Generic NETLINK
776  * mechanism.  Returns zero on success, negative values on failure.
777  *
778  */
779 int __init netlbl_mgmt_genl_init(void)
780 {
781         return genl_register_family_with_ops(&netlbl_mgmt_gnl_family,
782                 netlbl_mgmt_genl_ops, ARRAY_SIZE(netlbl_mgmt_genl_ops));
783 }