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