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