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