Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <netinet/in.h>
4 : #include <linux/if_ether.h>
5 : #include <linux/if_macsec.h>
6 : #include <linux/genetlink.h>
7 :
8 : #include "conf-parser.h"
9 : #include "fileio.h"
10 : #include "hashmap.h"
11 : #include "hexdecoct.h"
12 : #include "macsec.h"
13 : #include "memory-util.h"
14 : #include "missing.h"
15 : #include "netlink-util.h"
16 : #include "network-internal.h"
17 : #include "networkd-address.h"
18 : #include "networkd-manager.h"
19 : #include "path-util.h"
20 : #include "sd-netlink.h"
21 : #include "socket-util.h"
22 : #include "string-table.h"
23 : #include "string-util.h"
24 : #include "util.h"
25 :
26 0 : static void security_association_clear(SecurityAssociation *sa) {
27 0 : if (!sa)
28 0 : return;
29 :
30 0 : explicit_bzero_safe(sa->key, sa->key_len);
31 0 : free(sa->key);
32 0 : free(sa->key_file);
33 : }
34 :
35 0 : static void security_association_init(SecurityAssociation *sa) {
36 0 : assert(sa);
37 :
38 0 : sa->activate = -1;
39 0 : sa->use_for_encoding = -1;
40 0 : }
41 :
42 0 : static void macsec_receive_association_free(ReceiveAssociation *c) {
43 0 : if (!c)
44 0 : return;
45 :
46 0 : if (c->macsec && c->section)
47 0 : ordered_hashmap_remove(c->macsec->receive_associations_by_section, c->section);
48 :
49 0 : network_config_section_free(c->section);
50 0 : security_association_clear(&c->sa);
51 :
52 0 : free(c);
53 : }
54 :
55 0 : DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
56 :
57 0 : static int macsec_receive_association_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveAssociation **ret) {
58 0 : _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
59 0 : _cleanup_(macsec_receive_association_freep) ReceiveAssociation *c = NULL;
60 : int r;
61 :
62 0 : assert(s);
63 0 : assert(ret);
64 0 : assert(filename);
65 0 : assert(section_line > 0);
66 :
67 0 : r = network_config_section_new(filename, section_line, &n);
68 0 : if (r < 0)
69 0 : return r;
70 :
71 0 : c = ordered_hashmap_get(s->receive_associations_by_section, n);
72 0 : if (c) {
73 0 : *ret = TAKE_PTR(c);
74 0 : return 0;
75 : }
76 :
77 0 : c = new(ReceiveAssociation, 1);
78 0 : if (!c)
79 0 : return -ENOMEM;
80 :
81 0 : *c = (ReceiveAssociation) {
82 : .macsec = s,
83 0 : .section = TAKE_PTR(n),
84 : };
85 :
86 0 : security_association_init(&c->sa);
87 :
88 0 : r = ordered_hashmap_ensure_allocated(&s->receive_associations_by_section, &network_config_hash_ops);
89 0 : if (r < 0)
90 0 : return r;
91 :
92 0 : r = ordered_hashmap_put(s->receive_associations_by_section, c->section, c);
93 0 : if (r < 0)
94 0 : return r;
95 :
96 0 : *ret = TAKE_PTR(c);
97 :
98 0 : return 0;
99 : }
100 :
101 0 : static void macsec_receive_channel_free(ReceiveChannel *c) {
102 0 : if (!c)
103 0 : return;
104 :
105 0 : if (c->macsec) {
106 0 : if (c->sci.as_uint64 > 0)
107 0 : ordered_hashmap_remove(c->macsec->receive_channels, &c->sci.as_uint64);
108 :
109 0 : if (c->section)
110 0 : ordered_hashmap_remove(c->macsec->receive_channels_by_section, c->section);
111 : }
112 :
113 0 : network_config_section_free(c->section);
114 :
115 0 : free(c);
116 : }
117 :
118 0 : DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
119 :
120 0 : static int macsec_receive_channel_new(MACsec *s, uint64_t sci, ReceiveChannel **ret) {
121 : ReceiveChannel *c;
122 :
123 0 : assert(s);
124 :
125 0 : c = new(ReceiveChannel, 1);
126 0 : if (!c)
127 0 : return -ENOMEM;
128 :
129 0 : *c = (ReceiveChannel) {
130 : .macsec = s,
131 : .sci.as_uint64 = sci,
132 : };
133 :
134 0 : *ret = c;
135 0 : return 0;
136 : }
137 :
138 0 : static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) {
139 0 : _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
140 0 : _cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL;
141 : int r;
142 :
143 0 : assert(s);
144 0 : assert(ret);
145 0 : assert(filename);
146 0 : assert(section_line > 0);
147 :
148 0 : r = network_config_section_new(filename, section_line, &n);
149 0 : if (r < 0)
150 0 : return r;
151 :
152 0 : c = ordered_hashmap_get(s->receive_channels_by_section, n);
153 0 : if (c) {
154 0 : *ret = TAKE_PTR(c);
155 0 : return 0;
156 : }
157 :
158 0 : r = macsec_receive_channel_new(s, 0, &c);
159 0 : if (r < 0)
160 0 : return r;
161 :
162 0 : c->section = TAKE_PTR(n);
163 :
164 0 : r = ordered_hashmap_ensure_allocated(&s->receive_channels_by_section, &network_config_hash_ops);
165 0 : if (r < 0)
166 0 : return r;
167 :
168 0 : r = ordered_hashmap_put(s->receive_channels_by_section, c->section, c);
169 0 : if (r < 0)
170 0 : return r;
171 :
172 0 : *ret = TAKE_PTR(c);
173 :
174 0 : return 0;
175 : }
176 :
177 0 : static void macsec_transmit_association_free(TransmitAssociation *a) {
178 0 : if (!a)
179 0 : return;
180 :
181 0 : if (a->macsec && a->section)
182 0 : ordered_hashmap_remove(a->macsec->transmit_associations_by_section, a->section);
183 :
184 0 : network_config_section_free(a->section);
185 0 : security_association_clear(&a->sa);
186 :
187 0 : free(a);
188 : }
189 :
190 0 : DEFINE_NETWORK_SECTION_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
191 :
192 0 : static int macsec_transmit_association_new_static(MACsec *s, const char *filename, unsigned section_line, TransmitAssociation **ret) {
193 0 : _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
194 0 : _cleanup_(macsec_transmit_association_freep) TransmitAssociation *a = NULL;
195 : int r;
196 :
197 0 : assert(s);
198 0 : assert(ret);
199 0 : assert(filename);
200 0 : assert(section_line > 0);
201 :
202 0 : r = network_config_section_new(filename, section_line, &n);
203 0 : if (r < 0)
204 0 : return r;
205 :
206 0 : a = ordered_hashmap_get(s->transmit_associations_by_section, n);
207 0 : if (a) {
208 0 : *ret = TAKE_PTR(a);
209 0 : return 0;
210 : }
211 :
212 0 : a = new(TransmitAssociation, 1);
213 0 : if (!a)
214 0 : return -ENOMEM;
215 :
216 0 : *a = (TransmitAssociation) {
217 : .macsec = s,
218 0 : .section = TAKE_PTR(n),
219 : };
220 :
221 0 : security_association_init(&a->sa);
222 :
223 0 : r = ordered_hashmap_ensure_allocated(&s->transmit_associations_by_section, &network_config_hash_ops);
224 0 : if (r < 0)
225 0 : return r;
226 :
227 0 : r = ordered_hashmap_put(s->transmit_associations_by_section, a->section, a);
228 0 : if (r < 0)
229 0 : return r;
230 :
231 0 : *ret = TAKE_PTR(a);
232 :
233 0 : return 0;
234 : }
235 :
236 0 : static int netdev_macsec_fill_message(NetDev *netdev, int command, sd_netlink_message **ret) {
237 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
238 : int r;
239 :
240 0 : assert(netdev);
241 0 : assert(netdev->ifindex > 0);
242 :
243 0 : r = sd_genl_message_new(netdev->manager->genl, SD_GENL_MACSEC, command, &m);
244 0 : if (r < 0)
245 0 : return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
246 :
247 0 : r = sd_netlink_message_append_u32(m, MACSEC_ATTR_IFINDEX, netdev->ifindex);
248 0 : if (r < 0)
249 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_IFINDEX attribute: %m");
250 :
251 0 : *ret = TAKE_PTR(m);
252 :
253 0 : return 0;
254 : }
255 :
256 0 : static int netdev_macsec_fill_message_sci(NetDev *netdev, MACsecSCI *sci, sd_netlink_message *m) {
257 : int r;
258 :
259 0 : assert(netdev);
260 0 : assert(m);
261 0 : assert(sci);
262 :
263 0 : r = sd_netlink_message_open_container(m, MACSEC_ATTR_RXSC_CONFIG);
264 0 : if (r < 0)
265 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m");
266 :
267 0 : r = sd_netlink_message_append_u64(m, MACSEC_RXSC_ATTR_SCI, sci->as_uint64);
268 0 : if (r < 0)
269 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_RXSC_ATTR_SCI attribute: %m");
270 :
271 0 : r = sd_netlink_message_close_container(m);
272 0 : if (r < 0)
273 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m");
274 :
275 0 : return 0;
276 : }
277 :
278 0 : static int netdev_macsec_fill_message_sa(NetDev *netdev, SecurityAssociation *a, sd_netlink_message *m) {
279 : int r;
280 :
281 0 : assert(netdev);
282 0 : assert(a);
283 0 : assert(m);
284 :
285 0 : r = sd_netlink_message_open_container(m, MACSEC_ATTR_SA_CONFIG);
286 0 : if (r < 0)
287 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m");
288 :
289 0 : r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_AN, a->association_number);
290 0 : if (r < 0)
291 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_AN attribute: %m");
292 :
293 0 : if (a->packet_number > 0) {
294 0 : r = sd_netlink_message_append_u32(m, MACSEC_SA_ATTR_PN, a->packet_number);
295 0 : if (r < 0)
296 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_PN attribute: %m");
297 : }
298 :
299 0 : if (a->key_len > 0) {
300 0 : r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEYID, a->key_id, MACSEC_KEYID_LEN);
301 0 : if (r < 0)
302 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEYID attribute: %m");
303 :
304 0 : r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEY, a->key, a->key_len);
305 0 : if (r < 0)
306 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEY attribute: %m");
307 : }
308 :
309 0 : if (a->activate >= 0) {
310 0 : r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_ACTIVE, a->activate);
311 0 : if (r < 0)
312 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_ACTIVE attribute: %m");
313 : }
314 :
315 0 : r = sd_netlink_message_close_container(m);
316 0 : if (r < 0)
317 0 : return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m");
318 :
319 0 : return 0;
320 : }
321 :
322 0 : static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
323 : int r;
324 :
325 0 : assert(netdev);
326 0 : assert(netdev->state != _NETDEV_STATE_INVALID);
327 :
328 0 : r = sd_netlink_message_get_errno(m);
329 0 : if (r == -EEXIST)
330 0 : log_netdev_info(netdev,
331 : "MACsec receive secure association exists, "
332 : "using existing without changing its parameters");
333 0 : else if (r < 0) {
334 0 : log_netdev_warning_errno(netdev, r,
335 : "Failed to add receive secure association: %m");
336 0 : netdev_drop(netdev);
337 :
338 0 : return 1;
339 : }
340 :
341 0 : log_netdev_debug(netdev, "Receive secure association is configured");
342 :
343 0 : return 1;
344 : }
345 :
346 0 : static int netdev_macsec_configure_receive_association(NetDev *netdev, ReceiveAssociation *a) {
347 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
348 : int r;
349 :
350 0 : assert(netdev);
351 0 : assert(a);
352 :
353 0 : r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSA, &m);
354 0 : if (r < 0)
355 0 : return r;
356 :
357 0 : r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
358 0 : if (r < 0)
359 0 : return r;
360 :
361 0 : r = netdev_macsec_fill_message_sci(netdev, &a->sci, m);
362 0 : if (r < 0)
363 0 : return r;
364 :
365 0 : r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_association_handler,
366 : netdev_destroy_callback, netdev);
367 0 : if (r < 0)
368 0 : return log_netdev_error_errno(netdev, r, "Failed to configure receive secure association: %m");
369 :
370 0 : netdev_ref(netdev);
371 :
372 0 : return 0;
373 : }
374 :
375 0 : static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *m, ReceiveChannel *c) {
376 : NetDev *netdev;
377 : unsigned i;
378 : int r;
379 :
380 0 : assert(c);
381 0 : assert(c->macsec);
382 :
383 0 : netdev = NETDEV(c->macsec);
384 :
385 0 : assert(netdev->state != _NETDEV_STATE_INVALID);
386 :
387 0 : r = sd_netlink_message_get_errno(m);
388 0 : if (r == -EEXIST)
389 0 : log_netdev_debug(netdev,
390 : "MACsec receive channel exists, "
391 : "using existing without changing its parameters");
392 0 : else if (r < 0) {
393 0 : log_netdev_warning_errno(netdev, r,
394 : "Failed to add receive secure channel: %m");
395 0 : netdev_drop(netdev);
396 :
397 0 : return 1;
398 : }
399 :
400 0 : log_netdev_debug(netdev, "Receive channel is configured");
401 :
402 0 : for (i = 0; i < c->n_rxsa; i++) {
403 0 : r = netdev_macsec_configure_receive_association(netdev, c->rxsa[i]);
404 0 : if (r < 0) {
405 0 : log_netdev_warning_errno(netdev, r,
406 : "Failed to configure receive security association: %m");
407 0 : netdev_drop(netdev);
408 0 : return 1;
409 : }
410 : }
411 :
412 0 : return 1;
413 : }
414 :
415 0 : static void receive_channel_destroy_callback(ReceiveChannel *c) {
416 0 : assert(c);
417 0 : assert(c->macsec);
418 :
419 0 : netdev_unref(NETDEV(c->macsec));
420 0 : }
421 :
422 0 : static int netdev_macsec_configure_receive_channel(NetDev *netdev, ReceiveChannel *c) {
423 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
424 : int r;
425 :
426 0 : assert(netdev);
427 0 : assert(c);
428 :
429 0 : r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSC, &m);
430 0 : if (r < 0)
431 0 : return r;
432 :
433 0 : r = netdev_macsec_fill_message_sci(netdev, &c->sci, m);
434 0 : if (r < 0)
435 0 : return r;
436 :
437 0 : r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_channel_handler,
438 : receive_channel_destroy_callback, c);
439 0 : if (r < 0)
440 0 : return log_netdev_error_errno(netdev, r, "Failed to configure receive channel: %m");
441 :
442 0 : netdev_ref(netdev);
443 :
444 0 : return 0;
445 : }
446 :
447 0 : static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
448 : int r;
449 :
450 0 : assert(netdev);
451 0 : assert(netdev->state != _NETDEV_STATE_INVALID);
452 :
453 0 : r = sd_netlink_message_get_errno(m);
454 0 : if (r == -EEXIST)
455 0 : log_netdev_info(netdev,
456 : "MACsec transmit secure association exists, "
457 : "using existing without changing its parameters");
458 0 : else if (r < 0) {
459 0 : log_netdev_warning_errno(netdev, r,
460 : "Failed to add transmit secure association: %m");
461 0 : netdev_drop(netdev);
462 :
463 0 : return 1;
464 : }
465 :
466 0 : log_netdev_debug(netdev, "Transmit secure association is configured");
467 :
468 0 : return 1;
469 : }
470 :
471 0 : static int netdev_macsec_configure_transmit_association(NetDev *netdev, TransmitAssociation *a) {
472 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
473 : int r;
474 :
475 0 : assert(netdev);
476 0 : assert(a);
477 :
478 0 : r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_TXSA, &m);
479 0 : if (r < 0)
480 0 : return r;
481 :
482 0 : r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
483 0 : if (r < 0)
484 0 : return r;
485 :
486 0 : r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_transmit_association_handler,
487 : netdev_destroy_callback, netdev);
488 0 : if (r < 0)
489 0 : return log_netdev_error_errno(netdev, r, "Failed to configure transmit secure association: %m");
490 :
491 0 : netdev_ref(netdev);
492 :
493 0 : return 0;
494 : }
495 :
496 0 : static int netdev_macsec_configure(NetDev *netdev, Link *link, sd_netlink_message *m) {
497 : TransmitAssociation *a;
498 : ReceiveChannel *c;
499 : Iterator i;
500 : MACsec *s;
501 : int r;
502 :
503 0 : assert(netdev);
504 0 : s = MACSEC(netdev);
505 0 : assert(s);
506 :
507 0 : ORDERED_HASHMAP_FOREACH(a, s->transmit_associations_by_section, i) {
508 0 : r = netdev_macsec_configure_transmit_association(netdev, a);
509 0 : if (r < 0)
510 0 : return r;
511 : }
512 :
513 0 : ORDERED_HASHMAP_FOREACH(c, s->receive_channels, i) {
514 0 : r = netdev_macsec_configure_receive_channel(netdev, c);
515 0 : if (r < 0)
516 0 : return r;
517 : }
518 :
519 0 : return 0;
520 : }
521 :
522 0 : static int netdev_macsec_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
523 : MACsec *v;
524 : int r;
525 :
526 0 : assert(netdev);
527 0 : assert(m);
528 :
529 0 : v = MACSEC(netdev);
530 :
531 0 : if (v->port > 0) {
532 0 : r = sd_netlink_message_append_u16(m, IFLA_MACSEC_PORT, v->port);
533 0 : if (r < 0)
534 0 : return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_PORT attribute: %m");
535 : }
536 :
537 0 : if (v->encrypt >= 0) {
538 0 : r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCRYPT, v->encrypt);
539 0 : if (r < 0)
540 0 : return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCRYPT attribute: %m");
541 : }
542 :
543 0 : r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCODING_SA, v->encoding_an);
544 0 : if (r < 0)
545 0 : return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCODING_SA attribute: %m");
546 :
547 0 : return r;
548 : }
549 :
550 0 : int config_parse_macsec_port(
551 : const char *unit,
552 : const char *filename,
553 : unsigned line,
554 : const char *section,
555 : unsigned section_line,
556 : const char *lvalue,
557 : int ltype,
558 : const char *rvalue,
559 : void *data,
560 : void *userdata) {
561 :
562 0 : _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
563 0 : _cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
564 0 : MACsec *s = userdata;
565 : uint16_t port;
566 : void *dest;
567 : int r;
568 :
569 0 : assert(filename);
570 0 : assert(section);
571 0 : assert(lvalue);
572 0 : assert(rvalue);
573 0 : assert(data);
574 :
575 : /* This parses port used to make Secure Channel Identifier (SCI) */
576 :
577 0 : if (streq(section, "MACsec"))
578 0 : dest = &s->port;
579 0 : else if (streq(section, "MACsecReceiveChannel")) {
580 0 : r = macsec_receive_channel_new_static(s, filename, section_line, &c);
581 0 : if (r < 0)
582 0 : return r;
583 :
584 0 : dest = &c->sci.port;
585 : } else {
586 0 : assert(streq(section, "MACsecReceiveAssociation"));
587 :
588 0 : r = macsec_receive_association_new_static(s, filename, section_line, &b);
589 0 : if (r < 0)
590 0 : return r;
591 :
592 0 : dest = &b->sci.port;
593 : }
594 :
595 0 : r = parse_ip_port(rvalue, &port);
596 0 : if (r < 0) {
597 0 : log_syntax(unit, LOG_ERR, filename, line, r,
598 : "Failed to parse port '%s' for secure channel identifier. Ignoring assignment: %m",
599 : rvalue);
600 0 : return 0;
601 : }
602 :
603 0 : unaligned_write_be16(dest, port);
604 :
605 0 : TAKE_PTR(b);
606 0 : TAKE_PTR(c);
607 :
608 0 : return 0;
609 : }
610 :
611 0 : int config_parse_macsec_hw_address(
612 : const char *unit,
613 : const char *filename,
614 : unsigned line,
615 : const char *section,
616 : unsigned section_line,
617 : const char *lvalue,
618 : int ltype,
619 : const char *rvalue,
620 : void *data,
621 : void *userdata) {
622 :
623 0 : _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
624 0 : _cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
625 0 : MACsec *s = userdata;
626 : int r;
627 :
628 0 : assert(filename);
629 0 : assert(section);
630 0 : assert(lvalue);
631 0 : assert(rvalue);
632 0 : assert(data);
633 :
634 0 : if (streq(section, "MACsecReceiveChannel"))
635 0 : r = macsec_receive_channel_new_static(s, filename, section_line, &c);
636 : else
637 0 : r = macsec_receive_association_new_static(s, filename, section_line, &b);
638 0 : if (r < 0)
639 0 : return r;
640 :
641 0 : r = ether_addr_from_string(rvalue, b ? &b->sci.mac : &c->sci.mac);
642 0 : if (r < 0) {
643 0 : log_syntax(unit, LOG_ERR, filename, line, r,
644 : "Failed to parse MAC address for secure channel identifier. "
645 : "Ignoring assignment: %s", rvalue);
646 0 : return 0;
647 : }
648 :
649 0 : TAKE_PTR(b);
650 0 : TAKE_PTR(c);
651 :
652 0 : return 0;
653 : }
654 :
655 0 : int config_parse_macsec_packet_number(
656 : const char *unit,
657 : const char *filename,
658 : unsigned line,
659 : const char *section,
660 : unsigned section_line,
661 : const char *lvalue,
662 : int ltype,
663 : const char *rvalue,
664 : void *data,
665 : void *userdata) {
666 :
667 0 : _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
668 0 : _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
669 0 : MACsec *s = userdata;
670 : uint32_t val, *dest;
671 : int r;
672 :
673 0 : assert(filename);
674 0 : assert(section);
675 0 : assert(lvalue);
676 0 : assert(rvalue);
677 0 : assert(data);
678 :
679 0 : if (streq(section, "MACsecTransmitAssociation"))
680 0 : r = macsec_transmit_association_new_static(s, filename, section_line, &a);
681 : else
682 0 : r = macsec_receive_association_new_static(s, filename, section_line, &b);
683 0 : if (r < 0)
684 0 : return r;
685 :
686 0 : dest = a ? &a->sa.packet_number : &b->sa.packet_number;
687 :
688 0 : r = safe_atou32(rvalue, &val);
689 0 : if (r < 0) {
690 0 : log_syntax(unit, LOG_ERR, filename, line, r,
691 : "Failed to parse packet number. Ignoring assignment: %s", rvalue);
692 0 : return 0;
693 : }
694 0 : if (streq(section, "MACsecTransmitAssociation") && val == 0) {
695 0 : log_syntax(unit, LOG_ERR, filename, line, 0,
696 : "Invalid packet number. Ignoring assignment: %s", rvalue);
697 0 : return 0;
698 : }
699 :
700 0 : *dest = val;
701 0 : TAKE_PTR(a);
702 0 : TAKE_PTR(b);
703 :
704 0 : return 0;
705 : }
706 :
707 0 : int config_parse_macsec_key(
708 : const char *unit,
709 : const char *filename,
710 : unsigned line,
711 : const char *section,
712 : unsigned section_line,
713 : const char *lvalue,
714 : int ltype,
715 : const char *rvalue,
716 : void *data,
717 : void *userdata) {
718 :
719 0 : _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
720 0 : _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
721 0 : _cleanup_(erase_and_freep) void *p = NULL;
722 0 : MACsec *s = userdata;
723 : SecurityAssociation *dest;
724 : size_t l;
725 : int r;
726 :
727 0 : assert(filename);
728 0 : assert(section);
729 0 : assert(lvalue);
730 0 : assert(rvalue);
731 0 : assert(data);
732 :
733 0 : (void) warn_file_is_world_accessible(filename, NULL, unit, line);
734 :
735 0 : if (streq(section, "MACsecTransmitAssociation"))
736 0 : r = macsec_transmit_association_new_static(s, filename, section_line, &a);
737 : else
738 0 : r = macsec_receive_association_new_static(s, filename, section_line, &b);
739 0 : if (r < 0)
740 0 : return r;
741 :
742 0 : dest = a ? &a->sa : &b->sa;
743 :
744 0 : r = unhexmem_full(rvalue, strlen(rvalue), true, &p, &l);
745 0 : if (r < 0) {
746 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse key. Ignoring assignment: %m");
747 0 : return 0;
748 : }
749 :
750 0 : if (l != 16) {
751 : /* See DEFAULT_SAK_LEN in drivers/net/macsec.c */
752 0 : log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid key length (%zu). Ignoring assignment", l);
753 0 : return 0;
754 : }
755 :
756 0 : explicit_bzero_safe(dest->key, dest->key_len);
757 0 : free_and_replace(dest->key, p);
758 0 : dest->key_len = l;
759 :
760 0 : TAKE_PTR(a);
761 0 : TAKE_PTR(b);
762 :
763 0 : return 0;
764 : }
765 :
766 0 : int config_parse_macsec_key_file(
767 : const char *unit,
768 : const char *filename,
769 : unsigned line,
770 : const char *section,
771 : unsigned section_line,
772 : const char *lvalue,
773 : int ltype,
774 : const char *rvalue,
775 : void *data,
776 : void *userdata) {
777 :
778 0 : _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
779 0 : _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
780 0 : _cleanup_free_ char *path = NULL;
781 0 : MACsec *s = userdata;
782 : char **dest;
783 : int r;
784 :
785 0 : assert(filename);
786 0 : assert(section);
787 0 : assert(lvalue);
788 0 : assert(rvalue);
789 0 : assert(data);
790 :
791 0 : if (streq(section, "MACsecTransmitAssociation"))
792 0 : r = macsec_transmit_association_new_static(s, filename, section_line, &a);
793 : else
794 0 : r = macsec_receive_association_new_static(s, filename, section_line, &b);
795 0 : if (r < 0)
796 0 : return r;
797 :
798 0 : dest = a ? &a->sa.key_file : &b->sa.key_file;
799 :
800 0 : if (isempty(rvalue)) {
801 0 : *dest = mfree(*dest);
802 0 : return 0;
803 : }
804 :
805 0 : path = strdup(rvalue);
806 0 : if (!path)
807 0 : return log_oom();
808 :
809 0 : if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
810 0 : return 0;
811 :
812 0 : free_and_replace(*dest, path);
813 0 : TAKE_PTR(a);
814 0 : TAKE_PTR(b);
815 :
816 0 : return 0;
817 : }
818 :
819 0 : int config_parse_macsec_key_id(
820 : const char *unit,
821 : const char *filename,
822 : unsigned line,
823 : const char *section,
824 : unsigned section_line,
825 : const char *lvalue,
826 : int ltype,
827 : const char *rvalue,
828 : void *data,
829 : void *userdata) {
830 :
831 0 : _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
832 0 : _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
833 0 : _cleanup_free_ void *p;
834 0 : MACsec *s = userdata;
835 : uint8_t *dest;
836 : size_t l;
837 : int r;
838 :
839 0 : assert(filename);
840 0 : assert(section);
841 0 : assert(lvalue);
842 0 : assert(rvalue);
843 0 : assert(data);
844 :
845 0 : if (streq(section, "MACsecTransmitAssociation"))
846 0 : r = macsec_transmit_association_new_static(s, filename, section_line, &a);
847 : else
848 0 : r = macsec_receive_association_new_static(s, filename, section_line, &b);
849 0 : if (r < 0)
850 0 : return r;
851 :
852 0 : r = unhexmem(rvalue, strlen(rvalue), &p, &l);
853 0 : if (r < 0) {
854 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse KeyId \"%s\": %m", rvalue);
855 0 : return 0;
856 : }
857 0 : if (l > MACSEC_KEYID_LEN)
858 0 : return log_syntax(unit, LOG_ERR, filename, line, 0,
859 : "Specified KeyId is larger then the allowed maximum (%zu > %u), ignoring: %s",
860 : l, MACSEC_KEYID_LEN, rvalue);
861 :
862 0 : dest = a ? a->sa.key_id : b->sa.key_id;
863 0 : memcpy_safe(dest, p, l);
864 0 : memzero(dest + l, MACSEC_KEYID_LEN - l);
865 :
866 0 : TAKE_PTR(a);
867 0 : TAKE_PTR(b);
868 :
869 0 : return 0;
870 : }
871 :
872 0 : int config_parse_macsec_sa_activate(
873 : const char *unit,
874 : const char *filename,
875 : unsigned line,
876 : const char *section,
877 : unsigned section_line,
878 : const char *lvalue,
879 : int ltype,
880 : const char *rvalue,
881 : void *data,
882 : void *userdata) {
883 :
884 0 : _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
885 0 : _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
886 0 : MACsec *s = userdata;
887 : int *dest;
888 : int r;
889 :
890 0 : assert(filename);
891 0 : assert(section);
892 0 : assert(lvalue);
893 0 : assert(rvalue);
894 0 : assert(data);
895 :
896 0 : if (streq(section, "MACsecTransmitAssociation"))
897 0 : r = macsec_transmit_association_new_static(s, filename, section_line, &a);
898 : else
899 0 : r = macsec_receive_association_new_static(s, filename, section_line, &b);
900 0 : if (r < 0)
901 0 : return r;
902 :
903 0 : dest = a ? &a->sa.activate : &b->sa.activate;
904 :
905 0 : if (isempty(rvalue))
906 0 : r = -1;
907 : else {
908 0 : r = parse_boolean(rvalue);
909 0 : if (r < 0) {
910 0 : log_syntax(unit, LOG_ERR, filename, line, r,
911 : "Failed to parse activation mode of %s security association. "
912 : "Ignoring assignment: %s",
913 : streq(section, "MACsecTransmitAssociation") ? "transmit" : "receive",
914 : rvalue);
915 0 : return 0;
916 : }
917 : }
918 :
919 0 : *dest = r;
920 0 : TAKE_PTR(a);
921 0 : TAKE_PTR(b);
922 :
923 0 : return 0;
924 : }
925 :
926 0 : int config_parse_macsec_use_for_encoding(
927 : const char *unit,
928 : const char *filename,
929 : unsigned line,
930 : const char *section,
931 : unsigned section_line,
932 : const char *lvalue,
933 : int ltype,
934 : const char *rvalue,
935 : void *data,
936 : void *userdata) {
937 :
938 0 : _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
939 0 : MACsec *s = userdata;
940 : int r;
941 :
942 0 : assert(filename);
943 0 : assert(section);
944 0 : assert(lvalue);
945 0 : assert(rvalue);
946 0 : assert(data);
947 :
948 0 : r = macsec_transmit_association_new_static(s, filename, section_line, &a);
949 0 : if (r < 0)
950 0 : return r;
951 :
952 0 : if (isempty(rvalue))
953 0 : r = -1;
954 : else {
955 0 : r = parse_boolean(rvalue);
956 0 : if (r < 0) {
957 0 : log_syntax(unit, LOG_ERR, filename, line, r,
958 : "Failed to parse %s= setting. Ignoring assignment: %s",
959 : lvalue, rvalue);
960 0 : return 0;
961 : }
962 : }
963 :
964 0 : a->sa.use_for_encoding = r;
965 0 : if (a->sa.use_for_encoding > 0)
966 0 : a->sa.activate = true;
967 :
968 0 : TAKE_PTR(a);
969 :
970 0 : return 0;
971 : }
972 :
973 0 : static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
974 0 : _cleanup_(erase_and_freep) uint8_t *key = NULL;
975 : size_t key_len;
976 : int r;
977 :
978 0 : assert(netdev);
979 0 : assert(sa);
980 :
981 0 : if (!sa->key_file)
982 0 : return 0;
983 :
984 0 : r = read_full_file_full(sa->key_file, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len);
985 0 : if (r < 0)
986 0 : return log_netdev_error_errno(netdev, r,
987 : "Failed to read key from '%s', ignoring: %m",
988 : sa->key_file);
989 :
990 0 : if (key_len != 16)
991 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
992 : "Invalid key length (%zu bytes), ignoring: %m", key_len);
993 :
994 0 : explicit_bzero_safe(sa->key, sa->key_len);
995 0 : free_and_replace(sa->key, key);
996 0 : sa->key_len = key_len;
997 :
998 0 : return 0;
999 : }
1000 :
1001 0 : static int macsec_receive_channel_verify(ReceiveChannel *c) {
1002 : NetDev *netdev;
1003 : int r;
1004 :
1005 0 : assert(c);
1006 0 : assert(c->macsec);
1007 :
1008 0 : netdev = NETDEV(c->macsec);
1009 :
1010 0 : if (section_is_invalid(c->section))
1011 0 : return -EINVAL;
1012 :
1013 0 : if (ether_addr_is_null(&c->sci.mac))
1014 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1015 : "%s: MACsec receive channel without MAC address configured. "
1016 : "Ignoring [MACsecReceiveChannel] section from line %u",
1017 : c->section->filename, c->section->line);
1018 :
1019 0 : if (c->sci.port == 0)
1020 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1021 : "%s: MACsec receive channel without port configured. "
1022 : "Ignoring [MACsecReceiveChannel] section from line %u",
1023 : c->section->filename, c->section->line);
1024 :
1025 0 : r = ordered_hashmap_ensure_allocated(&c->macsec->receive_channels, &uint64_hash_ops);
1026 0 : if (r < 0)
1027 0 : return log_oom();
1028 :
1029 0 : r = ordered_hashmap_put(c->macsec->receive_channels, &c->sci.as_uint64, c);
1030 0 : if (r == -EEXIST)
1031 0 : return log_netdev_error_errno(netdev, r,
1032 : "%s: Multiple [MACsecReceiveChannel] sections have same SCI, "
1033 : "Ignoring [MACsecReceiveChannel] section from line %u",
1034 : c->section->filename, c->section->line);
1035 0 : if (r < 0)
1036 0 : return log_netdev_error_errno(netdev, r,
1037 : "%s: Failed to store [MACsecReceiveChannel] section at hashmap, "
1038 : "Ignoring [MACsecReceiveChannel] section from line %u",
1039 : c->section->filename, c->section->line);
1040 0 : return 0;
1041 : }
1042 :
1043 0 : static int macsec_transmit_association_verify(TransmitAssociation *t) {
1044 : NetDev *netdev;
1045 : int r;
1046 :
1047 0 : assert(t);
1048 0 : assert(t->macsec);
1049 :
1050 0 : netdev = NETDEV(t->macsec);
1051 :
1052 0 : if (section_is_invalid(t->section))
1053 0 : return -EINVAL;
1054 :
1055 0 : if (t->sa.packet_number == 0)
1056 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1057 : "%s: MACsec transmit secure association without PacketNumber= configured. "
1058 : "Ignoring [MACsecTransmitAssociation] section from line %u",
1059 : t->section->filename, t->section->line);
1060 :
1061 0 : r = macsec_read_key_file(netdev, &t->sa);
1062 0 : if (r < 0)
1063 0 : return r;
1064 :
1065 0 : if (t->sa.key_len <= 0)
1066 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1067 : "%s: MACsec transmit secure association without key configured. "
1068 : "Ignoring [MACsecTransmitAssociation] section from line %u",
1069 : t->section->filename, t->section->line);
1070 :
1071 0 : return 0;
1072 : }
1073 :
1074 0 : static int macsec_receive_association_verify(ReceiveAssociation *a) {
1075 : ReceiveChannel *c;
1076 : NetDev *netdev;
1077 : int r;
1078 :
1079 0 : assert(a);
1080 0 : assert(a->macsec);
1081 :
1082 0 : netdev = NETDEV(a->macsec);
1083 :
1084 0 : if (section_is_invalid(a->section))
1085 0 : return -EINVAL;
1086 :
1087 0 : r = macsec_read_key_file(netdev, &a->sa);
1088 0 : if (r < 0)
1089 0 : return r;
1090 :
1091 0 : if (a->sa.key_len <= 0)
1092 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1093 : "%s: MACsec receive secure association without key configured. "
1094 : "Ignoring [MACsecReceiveAssociation] section from line %u",
1095 : a->section->filename, a->section->line);
1096 :
1097 0 : if (ether_addr_is_null(&a->sci.mac))
1098 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1099 : "%s: MACsec receive secure association without MAC address configured. "
1100 : "Ignoring [MACsecReceiveAssociation] section from line %u",
1101 : a->section->filename, a->section->line);
1102 :
1103 0 : if (a->sci.port == 0)
1104 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1105 : "%s: MACsec receive secure association without port configured. "
1106 : "Ignoring [MACsecReceiveAssociation] section from line %u",
1107 : a->section->filename, a->section->line);
1108 :
1109 0 : c = ordered_hashmap_get(a->macsec->receive_channels, &a->sci.as_uint64);
1110 0 : if (!c) {
1111 0 : _cleanup_(macsec_receive_channel_freep) ReceiveChannel *new_channel = NULL;
1112 :
1113 0 : r = macsec_receive_channel_new(a->macsec, a->sci.as_uint64, &new_channel);
1114 0 : if (r < 0)
1115 0 : return log_oom();
1116 :
1117 0 : r = ordered_hashmap_ensure_allocated(&a->macsec->receive_channels, &uint64_hash_ops);
1118 0 : if (r < 0)
1119 0 : return log_oom();
1120 :
1121 0 : r = ordered_hashmap_put(a->macsec->receive_channels, &new_channel->sci.as_uint64, new_channel);
1122 0 : if (r < 0)
1123 0 : return log_netdev_error_errno(netdev, r,
1124 : "%s: Failed to store receive channel at hashmap, "
1125 : "Ignoring [MACsecReceiveAssociation] section from line %u",
1126 : a->section->filename, a->section->line);
1127 0 : c = TAKE_PTR(new_channel);
1128 : }
1129 0 : if (c->n_rxsa >= MACSEC_MAX_ASSOCIATION_NUMBER)
1130 0 : return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(ERANGE),
1131 : "%s: Too many [MACsecReceiveAssociation] sections for the same receive channel, "
1132 : "Ignoring [MACsecReceiveAssociation] section from line %u",
1133 : a->section->filename, a->section->line);
1134 :
1135 0 : a->sa.association_number = c->n_rxsa;
1136 0 : c->rxsa[c->n_rxsa++] = a;
1137 :
1138 0 : return 0;
1139 : }
1140 :
1141 0 : static int netdev_macsec_verify(NetDev *netdev, const char *filename) {
1142 0 : MACsec *v = MACSEC(netdev);
1143 : TransmitAssociation *a;
1144 : ReceiveAssociation *n;
1145 : ReceiveChannel *c;
1146 : Iterator i;
1147 : uint8_t an, encoding_an;
1148 : bool use_for_encoding;
1149 : int r;
1150 :
1151 0 : assert(netdev);
1152 0 : assert(v);
1153 0 : assert(filename);
1154 :
1155 0 : ORDERED_HASHMAP_FOREACH(c, v->receive_channels_by_section, i) {
1156 0 : r = macsec_receive_channel_verify(c);
1157 0 : if (r < 0)
1158 0 : macsec_receive_channel_free(c);
1159 : }
1160 :
1161 0 : an = 0;
1162 0 : use_for_encoding = false;
1163 0 : encoding_an = 0;
1164 0 : ORDERED_HASHMAP_FOREACH(a, v->transmit_associations_by_section, i) {
1165 0 : r = macsec_transmit_association_verify(a);
1166 0 : if (r < 0) {
1167 0 : macsec_transmit_association_free(a);
1168 0 : continue;
1169 : }
1170 :
1171 0 : if (an >= MACSEC_MAX_ASSOCIATION_NUMBER) {
1172 0 : log_netdev_error(netdev,
1173 : "%s: Too many [MACsecTransmitAssociation] sections configured. "
1174 : "Ignoring [MACsecTransmitAssociation] section from line %u",
1175 : a->section->filename, a->section->line);
1176 0 : macsec_transmit_association_free(a);
1177 0 : continue;
1178 : }
1179 :
1180 0 : a->sa.association_number = an++;
1181 :
1182 0 : if (a->sa.use_for_encoding > 0) {
1183 0 : if (use_for_encoding) {
1184 0 : log_netdev_warning(netdev,
1185 : "%s: Multiple security associations are set to be used for transmit channel."
1186 : "Disabling UseForEncoding= in [MACsecTransmitAssociation] section from line %u",
1187 : a->section->filename, a->section->line);
1188 0 : a->sa.use_for_encoding = false;
1189 : } else {
1190 0 : encoding_an = a->sa.association_number;
1191 0 : use_for_encoding = true;
1192 : }
1193 : }
1194 : }
1195 :
1196 0 : assert(encoding_an < MACSEC_MAX_ASSOCIATION_NUMBER);
1197 0 : v->encoding_an = encoding_an;
1198 :
1199 0 : ORDERED_HASHMAP_FOREACH(n, v->receive_associations_by_section, i) {
1200 0 : r = macsec_receive_association_verify(n);
1201 0 : if (r < 0)
1202 0 : macsec_receive_association_free(n);
1203 : }
1204 :
1205 0 : return 0;
1206 : }
1207 :
1208 0 : static void macsec_init(NetDev *netdev) {
1209 : MACsec *v;
1210 :
1211 0 : assert(netdev);
1212 :
1213 0 : v = MACSEC(netdev);
1214 :
1215 0 : assert(v);
1216 :
1217 0 : v->encrypt = -1;
1218 0 : }
1219 :
1220 0 : static void macsec_done(NetDev *netdev) {
1221 : MACsec *t;
1222 :
1223 0 : assert(netdev);
1224 :
1225 0 : t = MACSEC(netdev);
1226 :
1227 0 : assert(t);
1228 :
1229 0 : ordered_hashmap_free_with_destructor(t->receive_channels, macsec_receive_channel_free);
1230 0 : ordered_hashmap_free_with_destructor(t->receive_channels_by_section, macsec_receive_channel_free);
1231 0 : ordered_hashmap_free_with_destructor(t->transmit_associations_by_section, macsec_transmit_association_free);
1232 0 : ordered_hashmap_free_with_destructor(t->receive_associations_by_section, macsec_receive_association_free);
1233 0 : }
1234 :
1235 : const NetDevVTable macsec_vtable = {
1236 : .object_size = sizeof(MACsec),
1237 : .init = macsec_init,
1238 : .sections = "Match\0NetDev\0MACsec\0MACsecReceiveChannel\0MACsecTransmitAssociation\0MACsecReceiveAssociation\0",
1239 : .fill_message_create = netdev_macsec_fill_message_create,
1240 : .post_create = netdev_macsec_configure,
1241 : .done = macsec_done,
1242 : .create_type = NETDEV_CREATE_STACKED,
1243 : .config_verify = netdev_macsec_verify,
1244 : .generate_mac = true,
1245 : };
|