Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/netlabel/netlabel_mgmt.c
26282 views
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
}
843
844