diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 9b131a16b67..7ab9faac6da 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -931,6 +931,16 @@ dffafc8d7b9a43d5b9a3dfbbf6a30c16. + + KeyFile= + + Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal + string, which will be used in the transmission channel. When this option is specified, + Key= is ignored. Note that the file must be readable by the user + systemd-network, so it should be, e.g., owned by + root:systemd-network with a 0640 file mode. + + @@ -970,6 +980,12 @@ Accepts the same key in [MACsecTransmitAssociation] section. + + KeyFile= + + Accepts the same key in [MACsecTransmitAssociation] section. + + diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c index 01c9beaa07f..977c03eeb1b 100644 --- a/src/network/netdev/macsec.c +++ b/src/network/netdev/macsec.c @@ -6,6 +6,7 @@ #include #include "conf-parser.h" +#include "fileio.h" #include "hashmap.h" #include "hexdecoct.h" #include "macsec.h" @@ -15,6 +16,7 @@ #include "network-internal.h" #include "networkd-address.h" #include "networkd-manager.h" +#include "path-util.h" #include "sd-netlink.h" #include "socket-util.h" #include "string-table.h" @@ -27,6 +29,7 @@ static void security_association_clear(SecurityAssociation *sa) { explicit_bzero_safe(sa->key, sa->key_len); free(sa->key); + free(sa->key_file); } static void macsec_receive_association_free(ReceiveAssociation *c) { @@ -738,6 +741,59 @@ int config_parse_macsec_key( return 0; } +int 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) { + + _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL; + _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL; + _cleanup_free_ char *path = NULL; + MACsec *s = userdata; + char **dest; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(section, "MACsecTransmitAssociation")) + r = macsec_transmit_association_new_static(s, filename, section_line, &a); + else + r = macsec_receive_association_new_static(s, filename, section_line, &b); + if (r < 0) + return r; + + dest = a ? &a->sa.key_file : &b->sa.key_file; + + if (isempty(rvalue)) { + *dest = mfree(*dest); + return 0; + } + + path = strdup(rvalue); + if (!path) + return log_oom(); + + if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0) + return 0; + + free_and_replace(*dest, path); + TAKE_PTR(a); + TAKE_PTR(b); + + return 0; +} + int config_parse_macsec_key_id( const char *unit, const char *filename, @@ -793,6 +849,36 @@ int config_parse_macsec_key_id( return 0; } +static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) { + _cleanup_free_ uint8_t *key = NULL; + size_t key_len; + int r; + + assert(netdev); + assert(sa); + + if (!sa->key_file) + return 0; + + r = read_full_file_full(sa->key_file, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len); + if (r < 0) + return log_netdev_error_errno(netdev, r, + "Failed to read key from '%s', ignoring: %m", + sa->key_file); + if (key_len != 16) { + explicit_bzero_safe(key, key_len); + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "Invalid key length (%zu bytes), ignoring: %m", + key_len); + } + + explicit_bzero_safe(sa->key, sa->key_len); + free_and_replace(sa->key, key); + sa->key_len = key_len; + + return 0; +} + static int macsec_receive_channel_verify(ReceiveChannel *c) { NetDev *netdev; int r; @@ -837,6 +923,7 @@ static int macsec_receive_channel_verify(ReceiveChannel *c) { static int macsec_transmit_association_verify(TransmitAssociation *t) { NetDev *netdev; + int r; assert(t); assert(t->macsec); @@ -852,6 +939,10 @@ static int macsec_transmit_association_verify(TransmitAssociation *t) { "Ignoring [MACsecTransmitAssociation] section from line %u", t->section->filename, t->section->line); + r = macsec_read_key_file(netdev, &t->sa); + if (r < 0) + return r; + if (t->sa.key_len <= 0) return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "%s: MACsec transmit secure association without key configured. " @@ -874,6 +965,10 @@ static int macsec_receive_association_verify(ReceiveAssociation *a) { if (section_is_invalid(a->section)) return -EINVAL; + r = macsec_read_key_file(netdev, &a->sa); + if (r < 0) + return r; + if (a->sa.key_len <= 0) return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "%s: MACsec receive secure association without key configured. " diff --git a/src/network/netdev/macsec.h b/src/network/netdev/macsec.h index 55d95b475a2..36c90d47ab4 100644 --- a/src/network/netdev/macsec.h +++ b/src/network/netdev/macsec.h @@ -30,6 +30,7 @@ typedef struct SecurityAssociation { uint8_t key_id[MACSEC_KEYID_LEN]; uint8_t *key; uint32_t key_len; + char *key_file; } SecurityAssociation; typedef struct TransmitAssociation { @@ -76,3 +77,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_macsec_hw_address); CONFIG_PARSER_PROTOTYPE(config_parse_macsec_packet_number); CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key_id); CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key); +CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key_file); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 6dd8e10d597..d06ef23a8ba 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -140,11 +140,13 @@ MACsecReceiveChannel.MACAddress, config_parse_macsec_hw_address, 0, MACsecTransmitAssociation.PacketNumber, config_parse_macsec_packet_number, 0, 0 MACsecTransmitAssociation.KeyId, config_parse_macsec_key_id, 0, 0 MACsecTransmitAssociation.Key, config_parse_macsec_key, 0, 0 +MACsecTransmitAssociation.KeyFile, config_parse_macsec_key_file, 0, 0 MACsecReceiveAssociation.Port, config_parse_macsec_port, 0, 0 MACsecReceiveAssociation.MACAddress, config_parse_macsec_hw_address, 0, 0 MACsecReceiveAssociation.PacketNumber, config_parse_macsec_packet_number, 0, 0 MACsecReceiveAssociation.KeyId, config_parse_macsec_key_id, 0, 0 MACsecReceiveAssociation.Key, config_parse_macsec_key, 0, 0 +MACsecReceiveAssociation.KeyFile, config_parse_macsec_key_file, 0, 0 Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index 9ea34753030..344ffdf9b05 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -183,6 +183,7 @@ MACAddress= PacketNumber= KeyId= Key= +KeyFile= [MACsecReceiveChannel] Port= MACAddress= @@ -190,3 +191,4 @@ MACAddress= PacketNumber= KeyId= Key= +KeyFile=