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