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