Commit | Line | Data |
---|---|---|
d15c345f PM |
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.moore@hp.com> | |
9 | * | |
10 | */ | |
11 | ||
12 | /* | |
13 | * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 | |
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, write to the Free Software | |
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
28 | * | |
29 | */ | |
30 | ||
31 | #include <linux/types.h> | |
32 | #include <linux/socket.h> | |
33 | #include <linux/string.h> | |
34 | #include <linux/skbuff.h> | |
35 | #include <net/sock.h> | |
36 | #include <net/netlink.h> | |
37 | #include <net/genetlink.h> | |
38 | #include <net/netlabel.h> | |
39 | #include <net/cipso_ipv4.h> | |
40 | ||
41 | #include "netlabel_domainhash.h" | |
42 | #include "netlabel_user.h" | |
43 | #include "netlabel_mgmt.h" | |
44 | ||
23bcdc1a PM |
45 | /* NetLabel configured protocol count */ |
46 | static DEFINE_SPINLOCK(netlabel_mgmt_protocount_lock); | |
47 | static u32 netlabel_mgmt_protocount = 0; | |
48 | ||
fd385855 PM |
49 | /* Argument struct for netlbl_domhsh_walk() */ |
50 | struct netlbl_domhsh_walk_arg { | |
51 | struct netlink_callback *nl_cb; | |
52 | struct sk_buff *skb; | |
53 | u32 seq; | |
54 | }; | |
55 | ||
d15c345f PM |
56 | /* NetLabel Generic NETLINK CIPSOv4 family */ |
57 | static struct genl_family netlbl_mgmt_gnl_family = { | |
58 | .id = GENL_ID_GENERATE, | |
59 | .hdrsize = 0, | |
60 | .name = NETLBL_NLTYPE_MGMT_NAME, | |
61 | .version = NETLBL_PROTO_VERSION, | |
fd385855 | 62 | .maxattr = NLBL_MGMT_A_MAX, |
d15c345f PM |
63 | }; |
64 | ||
fd385855 | 65 | /* NetLabel Netlink attribute policy */ |
ef7c79ed | 66 | static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = { |
fd385855 PM |
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 | }; | |
d15c345f | 72 | |
23bcdc1a PM |
73 | /* |
74 | * NetLabel Misc Managment Functions | |
75 | */ | |
76 | ||
77 | /** | |
78 | * netlbl_mgmt_protocount_inc - Increment the configured labeled protocol count | |
79 | * | |
80 | * Description: | |
81 | * Increment the number of labeled protocol configurations in the current | |
82 | * NetLabel configuration. Keep track of this for use in determining if | |
83 | * NetLabel label enforcement should be active/enabled or not in the LSM. | |
84 | * | |
85 | */ | |
86 | void netlbl_mgmt_protocount_inc(void) | |
87 | { | |
88 | rcu_read_lock(); | |
89 | spin_lock(&netlabel_mgmt_protocount_lock); | |
90 | netlabel_mgmt_protocount++; | |
91 | spin_unlock(&netlabel_mgmt_protocount_lock); | |
92 | rcu_read_unlock(); | |
93 | } | |
94 | ||
95 | /** | |
96 | * netlbl_mgmt_protocount_dec - Decrement the configured labeled protocol count | |
97 | * | |
98 | * Description: | |
99 | * Decrement the number of labeled protocol configurations in the current | |
100 | * NetLabel configuration. Keep track of this for use in determining if | |
101 | * NetLabel label enforcement should be active/enabled or not in the LSM. | |
102 | * | |
103 | */ | |
104 | void netlbl_mgmt_protocount_dec(void) | |
105 | { | |
106 | rcu_read_lock(); | |
107 | spin_lock(&netlabel_mgmt_protocount_lock); | |
108 | if (netlabel_mgmt_protocount > 0) | |
109 | netlabel_mgmt_protocount--; | |
110 | spin_unlock(&netlabel_mgmt_protocount_lock); | |
111 | rcu_read_unlock(); | |
112 | } | |
113 | ||
114 | /** | |
115 | * netlbl_mgmt_protocount_value - Return the number of configured protocols | |
116 | * | |
117 | * Description: | |
118 | * Return the number of labeled protocols in the current NetLabel | |
119 | * configuration. This value is useful in determining if NetLabel label | |
120 | * enforcement should be active/enabled or not in the LSM. | |
121 | * | |
122 | */ | |
123 | u32 netlbl_mgmt_protocount_value(void) | |
124 | { | |
125 | u32 val; | |
126 | ||
127 | rcu_read_lock(); | |
128 | val = netlabel_mgmt_protocount; | |
129 | rcu_read_unlock(); | |
130 | ||
131 | return val; | |
132 | } | |
133 | ||
d15c345f PM |
134 | /* |
135 | * NetLabel Command Handlers | |
136 | */ | |
137 | ||
138 | /** | |
139 | * netlbl_mgmt_add - Handle an ADD message | |
140 | * @skb: the NETLINK buffer | |
141 | * @info: the Generic NETLINK info block | |
142 | * | |
143 | * Description: | |
144 | * Process a user generated ADD message and add the domains from the message | |
145 | * to the hash table. See netlabel.h for a description of the message format. | |
146 | * Returns zero on success, negative values on failure. | |
147 | * | |
148 | */ | |
149 | static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info) | |
150 | { | |
151 | int ret_val = -EINVAL; | |
d15c345f | 152 | struct netlbl_dom_map *entry = NULL; |
fd385855 | 153 | size_t tmp_size; |
d15c345f | 154 | u32 tmp_val; |
95d4e6be | 155 | struct netlbl_audit audit_info; |
d15c345f | 156 | |
fd385855 PM |
157 | if (!info->attrs[NLBL_MGMT_A_DOMAIN] || |
158 | !info->attrs[NLBL_MGMT_A_PROTOCOL]) | |
d15c345f PM |
159 | goto add_failure; |
160 | ||
95d4e6be PM |
161 | netlbl_netlink_auditinfo(skb, &audit_info); |
162 | ||
fd385855 PM |
163 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
164 | if (entry == NULL) { | |
165 | ret_val = -ENOMEM; | |
166 | goto add_failure; | |
167 | } | |
168 | tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]); | |
169 | entry->domain = kmalloc(tmp_size, GFP_KERNEL); | |
170 | if (entry->domain == NULL) { | |
171 | ret_val = -ENOMEM; | |
d15c345f | 172 | goto add_failure; |
fd385855 PM |
173 | } |
174 | entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); | |
175 | nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size); | |
d15c345f | 176 | |
fd385855 PM |
177 | switch (entry->type) { |
178 | case NETLBL_NLTYPE_UNLABELED: | |
95d4e6be | 179 | ret_val = netlbl_domhsh_add(entry, &audit_info); |
fd385855 PM |
180 | break; |
181 | case NETLBL_NLTYPE_CIPSOV4: | |
182 | if (!info->attrs[NLBL_MGMT_A_CV4DOI]) | |
d15c345f | 183 | goto add_failure; |
d15c345f | 184 | |
fd385855 PM |
185 | tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); |
186 | /* We should be holding a rcu_read_lock() here while we hold | |
187 | * the result but since the entry will always be deleted when | |
188 | * the CIPSO DOI is deleted we aren't going to keep the | |
189 | * lock. */ | |
190 | rcu_read_lock(); | |
191 | entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); | |
192 | if (entry->type_def.cipsov4 == NULL) { | |
d15c345f | 193 | rcu_read_unlock(); |
d15c345f | 194 | goto add_failure; |
fd385855 | 195 | } |
95d4e6be | 196 | ret_val = netlbl_domhsh_add(entry, &audit_info); |
fd385855 PM |
197 | rcu_read_unlock(); |
198 | break; | |
199 | default: | |
200 | goto add_failure; | |
d15c345f | 201 | } |
fd385855 PM |
202 | if (ret_val != 0) |
203 | goto add_failure; | |
d15c345f | 204 | |
d15c345f PM |
205 | return 0; |
206 | ||
207 | add_failure: | |
208 | if (entry) | |
209 | kfree(entry->domain); | |
210 | kfree(entry); | |
d15c345f PM |
211 | return ret_val; |
212 | } | |
213 | ||
214 | /** | |
215 | * netlbl_mgmt_remove - Handle a REMOVE message | |
216 | * @skb: the NETLINK buffer | |
217 | * @info: the Generic NETLINK info block | |
218 | * | |
219 | * Description: | |
220 | * Process a user generated REMOVE message and remove the specified domain | |
221 | * mappings. Returns zero on success, negative values on failure. | |
222 | * | |
223 | */ | |
224 | static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info) | |
225 | { | |
fd385855 | 226 | char *domain; |
95d4e6be | 227 | struct netlbl_audit audit_info; |
d15c345f | 228 | |
fd385855 PM |
229 | if (!info->attrs[NLBL_MGMT_A_DOMAIN]) |
230 | return -EINVAL; | |
d15c345f | 231 | |
95d4e6be PM |
232 | netlbl_netlink_auditinfo(skb, &audit_info); |
233 | ||
fd385855 | 234 | domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]); |
95d4e6be | 235 | return netlbl_domhsh_remove(domain, &audit_info); |
fd385855 PM |
236 | } |
237 | ||
238 | /** | |
239 | * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL | |
240 | * @entry: the domain mapping hash table entry | |
241 | * @arg: the netlbl_domhsh_walk_arg structure | |
242 | * | |
243 | * Description: | |
244 | * This function is designed to be used as a callback to the | |
245 | * netlbl_domhsh_walk() function for use in generating a response for a LISTALL | |
246 | * message. Returns the size of the message on success, negative values on | |
247 | * failure. | |
248 | * | |
249 | */ | |
250 | static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) | |
251 | { | |
252 | int ret_val = -ENOMEM; | |
253 | struct netlbl_domhsh_walk_arg *cb_arg = arg; | |
254 | void *data; | |
255 | ||
17c157c8 TG |
256 | data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, |
257 | cb_arg->seq, &netlbl_mgmt_gnl_family, | |
258 | NLM_F_MULTI, NLBL_MGMT_C_LISTALL); | |
fd385855 PM |
259 | if (data == NULL) |
260 | goto listall_cb_failure; | |
261 | ||
262 | ret_val = nla_put_string(cb_arg->skb, | |
263 | NLBL_MGMT_A_DOMAIN, | |
264 | entry->domain); | |
265 | if (ret_val != 0) | |
266 | goto listall_cb_failure; | |
267 | ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type); | |
268 | if (ret_val != 0) | |
269 | goto listall_cb_failure; | |
270 | switch (entry->type) { | |
271 | case NETLBL_NLTYPE_CIPSOV4: | |
272 | ret_val = nla_put_u32(cb_arg->skb, | |
273 | NLBL_MGMT_A_CV4DOI, | |
274 | entry->type_def.cipsov4->doi); | |
d15c345f | 275 | if (ret_val != 0) |
fd385855 PM |
276 | goto listall_cb_failure; |
277 | break; | |
d15c345f PM |
278 | } |
279 | ||
fd385855 PM |
280 | cb_arg->seq++; |
281 | return genlmsg_end(cb_arg->skb, data); | |
d15c345f | 282 | |
fd385855 PM |
283 | listall_cb_failure: |
284 | genlmsg_cancel(cb_arg->skb, data); | |
d15c345f PM |
285 | return ret_val; |
286 | } | |
287 | ||
288 | /** | |
fd385855 | 289 | * netlbl_mgmt_listall - Handle a LISTALL message |
d15c345f | 290 | * @skb: the NETLINK buffer |
fd385855 | 291 | * @cb: the NETLINK callback |
d15c345f PM |
292 | * |
293 | * Description: | |
fd385855 PM |
294 | * Process a user generated LISTALL message and dumps the domain hash table in |
295 | * a form suitable for use in a kernel generated LISTALL message. Returns zero | |
296 | * on success, negative values on failure. | |
d15c345f PM |
297 | * |
298 | */ | |
fd385855 PM |
299 | static int netlbl_mgmt_listall(struct sk_buff *skb, |
300 | struct netlink_callback *cb) | |
d15c345f | 301 | { |
fd385855 PM |
302 | struct netlbl_domhsh_walk_arg cb_arg; |
303 | u32 skip_bkt = cb->args[0]; | |
304 | u32 skip_chain = cb->args[1]; | |
305 | ||
306 | cb_arg.nl_cb = cb; | |
307 | cb_arg.skb = skb; | |
308 | cb_arg.seq = cb->nlh->nlmsg_seq; | |
309 | ||
310 | netlbl_domhsh_walk(&skip_bkt, | |
311 | &skip_chain, | |
312 | netlbl_mgmt_listall_cb, | |
313 | &cb_arg); | |
314 | ||
315 | cb->args[0] = skip_bkt; | |
316 | cb->args[1] = skip_chain; | |
317 | return skb->len; | |
d15c345f PM |
318 | } |
319 | ||
320 | /** | |
321 | * netlbl_mgmt_adddef - Handle an ADDDEF message | |
322 | * @skb: the NETLINK buffer | |
323 | * @info: the Generic NETLINK info block | |
324 | * | |
325 | * Description: | |
326 | * Process a user generated ADDDEF message and respond accordingly. Returns | |
327 | * zero on success, negative values on failure. | |
328 | * | |
329 | */ | |
330 | static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) | |
331 | { | |
332 | int ret_val = -EINVAL; | |
d15c345f PM |
333 | struct netlbl_dom_map *entry = NULL; |
334 | u32 tmp_val; | |
95d4e6be | 335 | struct netlbl_audit audit_info; |
d15c345f | 336 | |
fd385855 | 337 | if (!info->attrs[NLBL_MGMT_A_PROTOCOL]) |
d15c345f | 338 | goto adddef_failure; |
d15c345f | 339 | |
95d4e6be PM |
340 | netlbl_netlink_auditinfo(skb, &audit_info); |
341 | ||
d15c345f PM |
342 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
343 | if (entry == NULL) { | |
344 | ret_val = -ENOMEM; | |
345 | goto adddef_failure; | |
346 | } | |
fd385855 | 347 | entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); |
d15c345f | 348 | |
d15c345f PM |
349 | switch (entry->type) { |
350 | case NETLBL_NLTYPE_UNLABELED: | |
95d4e6be | 351 | ret_val = netlbl_domhsh_add_default(entry, &audit_info); |
d15c345f PM |
352 | break; |
353 | case NETLBL_NLTYPE_CIPSOV4: | |
fd385855 | 354 | if (!info->attrs[NLBL_MGMT_A_CV4DOI]) |
d15c345f | 355 | goto adddef_failure; |
fd385855 PM |
356 | |
357 | tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); | |
358 | /* We should be holding a rcu_read_lock() here while we hold | |
359 | * the result but since the entry will always be deleted when | |
360 | * the CIPSO DOI is deleted we aren't going to keep the | |
361 | * lock. */ | |
d15c345f PM |
362 | rcu_read_lock(); |
363 | entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); | |
364 | if (entry->type_def.cipsov4 == NULL) { | |
365 | rcu_read_unlock(); | |
d15c345f PM |
366 | goto adddef_failure; |
367 | } | |
95d4e6be | 368 | ret_val = netlbl_domhsh_add_default(entry, &audit_info); |
d15c345f PM |
369 | rcu_read_unlock(); |
370 | break; | |
371 | default: | |
fd385855 | 372 | goto adddef_failure; |
d15c345f PM |
373 | } |
374 | if (ret_val != 0) | |
375 | goto adddef_failure; | |
376 | ||
d15c345f PM |
377 | return 0; |
378 | ||
379 | adddef_failure: | |
380 | kfree(entry); | |
d15c345f PM |
381 | return ret_val; |
382 | } | |
383 | ||
384 | /** | |
385 | * netlbl_mgmt_removedef - Handle a REMOVEDEF message | |
386 | * @skb: the NETLINK buffer | |
387 | * @info: the Generic NETLINK info block | |
388 | * | |
389 | * Description: | |
390 | * Process a user generated REMOVEDEF message and remove the default domain | |
391 | * mapping. Returns zero on success, negative values on failure. | |
392 | * | |
393 | */ | |
394 | static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) | |
395 | { | |
95d4e6be PM |
396 | struct netlbl_audit audit_info; |
397 | ||
398 | netlbl_netlink_auditinfo(skb, &audit_info); | |
399 | ||
400 | return netlbl_domhsh_remove_default(&audit_info); | |
d15c345f PM |
401 | } |
402 | ||
403 | /** | |
404 | * netlbl_mgmt_listdef - Handle a LISTDEF message | |
405 | * @skb: the NETLINK buffer | |
406 | * @info: the Generic NETLINK info block | |
407 | * | |
408 | * Description: | |
409 | * Process a user generated LISTDEF message and dumps the default domain | |
410 | * mapping in a form suitable for use in a kernel generated LISTDEF message. | |
411 | * Returns zero on success, negative values on failure. | |
412 | * | |
413 | */ | |
414 | static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) | |
415 | { | |
416 | int ret_val = -ENOMEM; | |
fd385855 PM |
417 | struct sk_buff *ans_skb = NULL; |
418 | void *data; | |
419 | struct netlbl_dom_map *entry; | |
d15c345f | 420 | |
339bf98f | 421 | ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
d15c345f | 422 | if (ans_skb == NULL) |
fd385855 | 423 | return -ENOMEM; |
17c157c8 TG |
424 | data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, |
425 | 0, NLBL_MGMT_C_LISTDEF); | |
fd385855 | 426 | if (data == NULL) |
d15c345f | 427 | goto listdef_failure; |
d15c345f | 428 | |
fd385855 PM |
429 | rcu_read_lock(); |
430 | entry = netlbl_domhsh_getentry(NULL); | |
431 | if (entry == NULL) { | |
432 | ret_val = -ENOENT; | |
433 | goto listdef_failure_lock; | |
434 | } | |
435 | ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type); | |
d15c345f | 436 | if (ret_val != 0) |
fd385855 PM |
437 | goto listdef_failure_lock; |
438 | switch (entry->type) { | |
439 | case NETLBL_NLTYPE_CIPSOV4: | |
440 | ret_val = nla_put_u32(ans_skb, | |
441 | NLBL_MGMT_A_CV4DOI, | |
442 | entry->type_def.cipsov4->doi); | |
443 | if (ret_val != 0) | |
444 | goto listdef_failure_lock; | |
445 | break; | |
446 | } | |
447 | rcu_read_unlock(); | |
d15c345f | 448 | |
fd385855 PM |
449 | genlmsg_end(ans_skb, data); |
450 | ||
81878d27 | 451 | ret_val = genlmsg_reply(ans_skb, info); |
fd385855 PM |
452 | if (ret_val != 0) |
453 | goto listdef_failure; | |
d15c345f PM |
454 | return 0; |
455 | ||
fd385855 PM |
456 | listdef_failure_lock: |
457 | rcu_read_unlock(); | |
d15c345f | 458 | listdef_failure: |
fd385855 | 459 | kfree_skb(ans_skb); |
d15c345f PM |
460 | return ret_val; |
461 | } | |
462 | ||
463 | /** | |
fd385855 PM |
464 | * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response |
465 | * @skb: the skb to write to | |
466 | * @seq: the NETLINK sequence number | |
467 | * @cb: the NETLINK callback | |
468 | * @protocol: the NetLabel protocol to use in the message | |
d15c345f PM |
469 | * |
470 | * Description: | |
fd385855 PM |
471 | * This function is to be used in conjunction with netlbl_mgmt_protocols() to |
472 | * answer a application's PROTOCOLS message. Returns the size of the message | |
473 | * on success, negative values on failure. | |
d15c345f PM |
474 | * |
475 | */ | |
fd385855 PM |
476 | static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, |
477 | struct netlink_callback *cb, | |
478 | u32 protocol) | |
d15c345f PM |
479 | { |
480 | int ret_val = -ENOMEM; | |
fd385855 PM |
481 | void *data; |
482 | ||
17c157c8 TG |
483 | data = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, |
484 | &netlbl_mgmt_gnl_family, NLM_F_MULTI, | |
485 | NLBL_MGMT_C_PROTOCOLS); | |
fd385855 PM |
486 | if (data == NULL) |
487 | goto protocols_cb_failure; | |
488 | ||
489 | ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol); | |
d15c345f | 490 | if (ret_val != 0) |
fd385855 | 491 | goto protocols_cb_failure; |
d15c345f | 492 | |
fd385855 | 493 | return genlmsg_end(skb, data); |
d15c345f | 494 | |
fd385855 PM |
495 | protocols_cb_failure: |
496 | genlmsg_cancel(skb, data); | |
d15c345f PM |
497 | return ret_val; |
498 | } | |
499 | ||
fd385855 PM |
500 | /** |
501 | * netlbl_mgmt_protocols - Handle a PROTOCOLS message | |
502 | * @skb: the NETLINK buffer | |
503 | * @cb: the NETLINK callback | |
504 | * | |
505 | * Description: | |
506 | * Process a user generated PROTOCOLS message and respond accordingly. | |
507 | * | |
508 | */ | |
509 | static int netlbl_mgmt_protocols(struct sk_buff *skb, | |
510 | struct netlink_callback *cb) | |
511 | { | |
512 | u32 protos_sent = cb->args[0]; | |
513 | ||
514 | if (protos_sent == 0) { | |
515 | if (netlbl_mgmt_protocols_cb(skb, | |
516 | cb, | |
517 | NETLBL_NLTYPE_UNLABELED) < 0) | |
518 | goto protocols_return; | |
519 | protos_sent++; | |
520 | } | |
521 | if (protos_sent == 1) { | |
522 | if (netlbl_mgmt_protocols_cb(skb, | |
523 | cb, | |
524 | NETLBL_NLTYPE_CIPSOV4) < 0) | |
525 | goto protocols_return; | |
526 | protos_sent++; | |
527 | } | |
528 | ||
529 | protocols_return: | |
530 | cb->args[0] = protos_sent; | |
531 | return skb->len; | |
532 | } | |
533 | ||
d15c345f PM |
534 | /** |
535 | * netlbl_mgmt_version - Handle a VERSION message | |
536 | * @skb: the NETLINK buffer | |
537 | * @info: the Generic NETLINK info block | |
538 | * | |
539 | * Description: | |
540 | * Process a user generated VERSION message and respond accordingly. Returns | |
541 | * zero on success, negative values on failure. | |
542 | * | |
543 | */ | |
544 | static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info) | |
545 | { | |
546 | int ret_val = -ENOMEM; | |
547 | struct sk_buff *ans_skb = NULL; | |
fd385855 | 548 | void *data; |
d15c345f | 549 | |
339bf98f | 550 | ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
d15c345f | 551 | if (ans_skb == NULL) |
fd385855 | 552 | return -ENOMEM; |
17c157c8 TG |
553 | data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, |
554 | 0, NLBL_MGMT_C_VERSION); | |
fd385855 | 555 | if (data == NULL) |
d15c345f PM |
556 | goto version_failure; |
557 | ||
fd385855 PM |
558 | ret_val = nla_put_u32(ans_skb, |
559 | NLBL_MGMT_A_VERSION, | |
560 | NETLBL_PROTO_VERSION); | |
d15c345f PM |
561 | if (ret_val != 0) |
562 | goto version_failure; | |
563 | ||
fd385855 PM |
564 | genlmsg_end(ans_skb, data); |
565 | ||
81878d27 | 566 | ret_val = genlmsg_reply(ans_skb, info); |
d15c345f PM |
567 | if (ret_val != 0) |
568 | goto version_failure; | |
d15c345f PM |
569 | return 0; |
570 | ||
571 | version_failure: | |
572 | kfree_skb(ans_skb); | |
d15c345f PM |
573 | return ret_val; |
574 | } | |
575 | ||
576 | ||
577 | /* | |
578 | * NetLabel Generic NETLINK Command Definitions | |
579 | */ | |
580 | ||
581 | static struct genl_ops netlbl_mgmt_genl_c_add = { | |
582 | .cmd = NLBL_MGMT_C_ADD, | |
fd385855 PM |
583 | .flags = GENL_ADMIN_PERM, |
584 | .policy = netlbl_mgmt_genl_policy, | |
d15c345f PM |
585 | .doit = netlbl_mgmt_add, |
586 | .dumpit = NULL, | |
587 | }; | |
588 | ||
589 | static struct genl_ops netlbl_mgmt_genl_c_remove = { | |
590 | .cmd = NLBL_MGMT_C_REMOVE, | |
fd385855 PM |
591 | .flags = GENL_ADMIN_PERM, |
592 | .policy = netlbl_mgmt_genl_policy, | |
d15c345f PM |
593 | .doit = netlbl_mgmt_remove, |
594 | .dumpit = NULL, | |
595 | }; | |
596 | ||
fd385855 PM |
597 | static struct genl_ops netlbl_mgmt_genl_c_listall = { |
598 | .cmd = NLBL_MGMT_C_LISTALL, | |
d15c345f | 599 | .flags = 0, |
fd385855 PM |
600 | .policy = netlbl_mgmt_genl_policy, |
601 | .doit = NULL, | |
602 | .dumpit = netlbl_mgmt_listall, | |
d15c345f PM |
603 | }; |
604 | ||
605 | static struct genl_ops netlbl_mgmt_genl_c_adddef = { | |
606 | .cmd = NLBL_MGMT_C_ADDDEF, | |
fd385855 PM |
607 | .flags = GENL_ADMIN_PERM, |
608 | .policy = netlbl_mgmt_genl_policy, | |
d15c345f PM |
609 | .doit = netlbl_mgmt_adddef, |
610 | .dumpit = NULL, | |
611 | }; | |
612 | ||
613 | static struct genl_ops netlbl_mgmt_genl_c_removedef = { | |
614 | .cmd = NLBL_MGMT_C_REMOVEDEF, | |
fd385855 PM |
615 | .flags = GENL_ADMIN_PERM, |
616 | .policy = netlbl_mgmt_genl_policy, | |
d15c345f PM |
617 | .doit = netlbl_mgmt_removedef, |
618 | .dumpit = NULL, | |
619 | }; | |
620 | ||
621 | static struct genl_ops netlbl_mgmt_genl_c_listdef = { | |
622 | .cmd = NLBL_MGMT_C_LISTDEF, | |
623 | .flags = 0, | |
fd385855 | 624 | .policy = netlbl_mgmt_genl_policy, |
d15c345f PM |
625 | .doit = netlbl_mgmt_listdef, |
626 | .dumpit = NULL, | |
627 | }; | |
628 | ||
fd385855 PM |
629 | static struct genl_ops netlbl_mgmt_genl_c_protocols = { |
630 | .cmd = NLBL_MGMT_C_PROTOCOLS, | |
d15c345f | 631 | .flags = 0, |
fd385855 PM |
632 | .policy = netlbl_mgmt_genl_policy, |
633 | .doit = NULL, | |
634 | .dumpit = netlbl_mgmt_protocols, | |
d15c345f PM |
635 | }; |
636 | ||
637 | static struct genl_ops netlbl_mgmt_genl_c_version = { | |
638 | .cmd = NLBL_MGMT_C_VERSION, | |
639 | .flags = 0, | |
fd385855 | 640 | .policy = netlbl_mgmt_genl_policy, |
d15c345f PM |
641 | .doit = netlbl_mgmt_version, |
642 | .dumpit = NULL, | |
643 | }; | |
644 | ||
645 | /* | |
646 | * NetLabel Generic NETLINK Protocol Functions | |
647 | */ | |
648 | ||
649 | /** | |
650 | * netlbl_mgmt_genl_init - Register the NetLabel management component | |
651 | * | |
652 | * Description: | |
653 | * Register the NetLabel management component with the Generic NETLINK | |
654 | * mechanism. Returns zero on success, negative values on failure. | |
655 | * | |
656 | */ | |
657 | int netlbl_mgmt_genl_init(void) | |
658 | { | |
659 | int ret_val; | |
660 | ||
661 | ret_val = genl_register_family(&netlbl_mgmt_gnl_family); | |
662 | if (ret_val != 0) | |
663 | return ret_val; | |
664 | ||
665 | ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, | |
666 | &netlbl_mgmt_genl_c_add); | |
667 | if (ret_val != 0) | |
668 | return ret_val; | |
669 | ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, | |
670 | &netlbl_mgmt_genl_c_remove); | |
671 | if (ret_val != 0) | |
672 | return ret_val; | |
673 | ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, | |
fd385855 | 674 | &netlbl_mgmt_genl_c_listall); |
d15c345f PM |
675 | if (ret_val != 0) |
676 | return ret_val; | |
677 | ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, | |
678 | &netlbl_mgmt_genl_c_adddef); | |
679 | if (ret_val != 0) | |
680 | return ret_val; | |
681 | ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, | |
682 | &netlbl_mgmt_genl_c_removedef); | |
683 | if (ret_val != 0) | |
684 | return ret_val; | |
685 | ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, | |
686 | &netlbl_mgmt_genl_c_listdef); | |
687 | if (ret_val != 0) | |
688 | return ret_val; | |
689 | ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, | |
fd385855 | 690 | &netlbl_mgmt_genl_c_protocols); |
d15c345f PM |
691 | if (ret_val != 0) |
692 | return ret_val; | |
693 | ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, | |
694 | &netlbl_mgmt_genl_c_version); | |
695 | if (ret_val != 0) | |
696 | return ret_val; | |
697 | ||
698 | return 0; | |
699 | } |