]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev/net: introduce WakeOnLanPassword= 20926/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 6 Oct 2021 12:49:58 +0000 (21:49 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 6 Oct 2021 16:50:37 +0000 (01:50 +0900)
Closes #20913.

man/systemd.link.xml
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
test/fuzz/fuzz-link-parser/directives.link

index c8d3c5137459604a8c5257bec58fcfb3823020e4..487f314396c3f6453962f5b744216a3c52beffa9 100644 (file)
             <varlistentry>
               <term><option>secureon</option></term>
               <listitem>
-                <para>Enable secureon(tm) password for MagicPacket(tm).
-                </para>
+                <para>Enable SecureOn password for MagicPacket. Implied when
+                <varname>WakeOnLanPassword=</varname> is specified. If specified without
+                <varname>WakeOnLanPassword=</varname> option, then the password is read from the
+                credential <literal><replaceable>LINK</replaceable>.link.wol.password</literal> (e.g.,
+                <literal>60-foo.link.wol.password</literal>), and if the credential not found, then
+                read from <literal>wol.password</literal>. See
+                <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> in
+                <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+                for details. The password in the credential, must be 6 bytes in hex format with each
+                byte separated by a colon (<literal>:</literal>) like an Ethernet MAC address, e.g.,
+                <literal>aa:bb:cc:dd:ee:ff</literal>.</para>
               </listitem>
             </varlistentry>
           </variablelist>
           cleared.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>WakeOnLanPassword=</varname></term>
+        <listitem>
+          <para>Specifies the SecureOn password for MagicPacket. Takes an absolute path to a regular
+          file or an <constant>AF_UNIX</constant> stream socket, or the plain password. When a path to
+          a regular file is specified, the password is read from it. When an
+          <constant>AF_UNIX</constant> stream socket is specified, a connection is made to it and the
+          password is read from it. The password must be 6 bytes in hex format with each byte separated
+          by a colon (<literal>:</literal>) like an Ethernet MAC address, e.g.,
+          <literal>aa:bb:cc:dd:ee:ff</literal>. This implies <varname>WakeOnLan=secureon</varname>.
+          Defaults to unset, and the current value will not be changed.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>Port=</varname></term>
         <listitem>
index 04c255ce514cb024905ea0571477f513100f9257..a9c94b48ed93aac500651be6fbf2f794a9cbbf22 100644 (file)
@@ -49,6 +49,7 @@ Link.BitsPerSecond,                        config_parse_si_uint64,
 Link.Duplex,                               config_parse_duplex,                   0,                             offsetof(LinkConfig, duplex)
 Link.AutoNegotiation,                      config_parse_tristate,                 0,                             offsetof(LinkConfig, autonegotiation)
 Link.WakeOnLan,                            config_parse_wol,                      0,                             offsetof(LinkConfig, wol)
+Link.WakeOnLanPassword,                    config_parse_wol_password,             0,                             0
 Link.Port,                                 config_parse_port,                     0,                             offsetof(LinkConfig, port)
 Link.ReceiveChecksumOffload,               config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_RXCSUM])
 Link.TransmitChecksumOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TXCSUM])
index 4802a0bf3ee1c6da76d106ba70121ce2be2e6c77..1ce8e471e7697516d116e5bd48ecdc54590f9704 100644 (file)
 #include "alloc-util.h"
 #include "conf-files.h"
 #include "conf-parser.h"
+#include "creds-util.h"
 #include "def.h"
 #include "device-private.h"
 #include "device-util.h"
 #include "ethtool-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "link-config.h"
 #include "log.h"
 #include "memory-util.h"
@@ -56,6 +58,8 @@ static LinkConfig* link_config_free(LinkConfig *link) {
         strv_free(link->alternative_names);
         free(link->alternative_names_policy);
         free(link->alias);
+        free(link->wol_password_file);
+        erase_and_free(link->wol_password);
 
         return mfree(link);
 }
@@ -101,6 +105,108 @@ int link_config_ctx_new(LinkConfigContext **ret) {
         return 0;
 }
 
+static int link_parse_wol_password(LinkConfig *link, const char *str) {
+        _cleanup_(erase_and_freep) uint8_t *p = NULL;
+        int r;
+
+        assert(link);
+        assert(str);
+
+        assert_cc(sizeof(struct ether_addr) == SOPASS_MAX);
+
+        p = new(uint8_t, SOPASS_MAX);
+        if (!p)
+                return -ENOMEM;
+
+        /* Reuse ether_addr_from_string(), as their formats are equivalent. */
+        r = ether_addr_from_string(str, (struct ether_addr*) p);
+        if (r < 0)
+                return r;
+
+        erase_and_free(link->wol_password);
+        link->wol_password = TAKE_PTR(p);
+        return 0;
+}
+
+static int link_read_wol_password_from_file(LinkConfig *link) {
+        _cleanup_(erase_and_freep) char *password = NULL;
+        int r;
+
+        assert(link);
+
+        if (!link->wol_password_file)
+                return 0;
+
+        r = read_full_file_full(
+                        AT_FDCWD, link->wol_password_file, UINT64_MAX, SIZE_MAX,
+                        READ_FULL_FILE_SECURE | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
+                        NULL, &password, NULL);
+        if (r < 0)
+                return r;
+
+        return link_parse_wol_password(link, password);
+}
+
+static int link_read_wol_password_from_cred(LinkConfig *link) {
+        _cleanup_free_ char *base = NULL, *cred_name = NULL;
+        _cleanup_(erase_and_freep) char *password = NULL;
+        int r;
+
+        assert(link);
+        assert(link->filename);
+
+        if (link->wol == UINT32_MAX)
+                return 0; /* WakeOnLan= is not specified. */
+        if (!FLAGS_SET(link->wol, WAKE_MAGICSECURE))
+                return 0; /* secureon is not specified in WakeOnLan=. */
+        if (link->wol_password)
+                return 0; /* WakeOnLanPassword= is specified. */
+        if (link->wol_password_file)
+                return 0; /* a file name is specified in WakeOnLanPassword=, but failed to read it. */
+
+        r = path_extract_filename(link->filename, &base);
+        if (r < 0)
+                return r;
+
+        cred_name = strjoin(base, ".wol.password");
+        if (!cred_name)
+                return -ENOMEM;
+
+        r = read_credential(cred_name, (void**) &password, NULL);
+        if (r == -ENOENT)
+                r = read_credential("wol.password", (void**) &password, NULL);
+        if (r < 0)
+                return r;
+
+        return link_parse_wol_password(link, password);
+}
+
+static int link_adjust_wol_options(LinkConfig *link) {
+        int r;
+
+        assert(link);
+
+        r = link_read_wol_password_from_file(link);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0)
+                log_warning_errno(r, "Failed to read WakeOnLan password from %s, ignoring: %m", link->wol_password_file);
+
+        r = link_read_wol_password_from_cred(link);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0)
+                log_warning_errno(r, "Failed to read WakeOnLan password from credential, ignoring: %m");
+
+        if (link->wol != UINT32_MAX && link->wol_password)
+                /* Enable WAKE_MAGICSECURE flag when WakeOnLanPassword=. Note that when
+                 * WakeOnLanPassword= is set without WakeOnLan=, then ethtool_set_wol() enables
+                 * WAKE_MAGICSECURE flag and other flags are not changed. */
+                link->wol |= WAKE_MAGICSECURE;
+
+        return 0;
+}
+
 int link_load_one(LinkConfigContext *ctx, const char *filename) {
         _cleanup_(link_config_freep) LinkConfig *link = NULL;
         _cleanup_free_ char *name = NULL;
@@ -177,6 +283,10 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
                 link->mac = mfree(link->mac);
         }
 
+        r = link_adjust_wol_options(link);
+        if (r < 0)
+                return r;
+
         log_debug("Parsed configuration file %s", filename);
 
         LIST_PREPEND(links, ctx->links, TAKE_PTR(link));
@@ -329,7 +439,7 @@ static int link_config_apply_ethtool_settings(int *ethtool_fd, const LinkConfig
                                                  port_to_string(config->port));
         }
 
-        r = ethtool_set_wol(ethtool_fd, name, config->wol, NULL);
+        r = ethtool_set_wol(ethtool_fd, name, config->wol, config->wol_password);
         if (r < 0) {
                 _cleanup_free_ char *str = NULL;
 
@@ -782,6 +892,52 @@ int config_parse_txqueuelen(
         return 0;
 }
 
+int config_parse_wol_password(
+                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) {
+
+        LinkConfig *link = userdata;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(userdata);
+
+        if (isempty(rvalue)) {
+                link->wol_password = erase_and_free(link->wol_password);
+                link->wol_password_file = mfree(link->wol_password_file);
+                return 0;
+        }
+
+        if (path_is_absolute(rvalue) && path_is_safe(rvalue)) {
+                link->wol_password = erase_and_free(link->wol_password);
+                return free_and_strdup_warn(&link->wol_password_file, rvalue);
+        }
+
+        warn_file_is_world_accessible(filename, NULL, unit, line);
+
+        r = link_parse_wol_password(link, rvalue);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s.", lvalue, rvalue);
+                return 0;
+        }
+
+        link->wol_password_file = mfree(link->wol_password_file);
+        return 0;
+}
+
 static const char* const mac_address_policy_table[_MAC_ADDRESS_POLICY_MAX] = {
         [MAC_ADDRESS_POLICY_PERSISTENT] = "persistent",
         [MAC_ADDRESS_POLICY_RANDOM] = "random",
index 38a02a75b90d4aa9582aa77345ae6daa0501e897..cf5eccf0939f3753a789f393bfd9d007be0e28e7 100644 (file)
@@ -58,6 +58,8 @@ struct LinkConfig {
         int autonegotiation;
         uint32_t advertise[N_ADVERTISE];
         uint32_t wol;
+        char *wol_password_file;
+        uint8_t *wol_password;
         NetDevPort port;
         int features[_NET_DEV_FEAT_MAX];
         netdev_channels channels;
@@ -97,6 +99,7 @@ const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN
 CONFIG_PARSER_PROTOTYPE(config_parse_ifalias);
 CONFIG_PARSER_PROTOTYPE(config_parse_rx_tx_queues);
 CONFIG_PARSER_PROTOTYPE(config_parse_txqueuelen);
+CONFIG_PARSER_PROTOTYPE(config_parse_wol_password);
 CONFIG_PARSER_PROTOTYPE(config_parse_mac_address_policy);
 CONFIG_PARSER_PROTOTYPE(config_parse_name_policy);
 CONFIG_PARSER_PROTOTYPE(config_parse_alternative_names_policy);
index 8be2434665a7ba5ffb55bac995e067b2fa3b9871..7586e35b4930e8e90c9c5a1fabf7f75c121ad32f 100644 (file)
@@ -28,6 +28,7 @@ BitsPerSecond=
 Duplex=
 AutoNegotiation=
 WakeOnLan=
+WakeOnLanPassword=
 Port=
 ReceiveChecksumOffload=
 TransmitChecksumOffload=