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