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