]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
ext_password: Implement new file-based backend
authorPatrick Steinhardt <ps@pks.im>
Sun, 14 Feb 2021 11:16:33 +0000 (12:16 +0100)
committerJouni Malinen <j@w1.fi>
Mon, 15 Feb 2021 22:47:43 +0000 (00:47 +0200)
It was not easily possible to separate configuration of an interface and
credentials when using the configuration file instead of the control
interface or D-Bus interface for setting up the network profiles. This
makes it hard to distribute configuration across a set of nodes which
use wpa_supplicant without also having to store credentials in the same
file. While this can be solved via scripting, having a native way to
achieve this would be preferable.

Turns out there already is a framework to have external password
storages. It only had a single "test" backend though, which is kind of
an in-memory store which gets initialized with all passwords up front
and is mainly for testing purposes. This isn't really suitable for the
above use case: the backend cannot be initialized as part of the central
configuration given that it needs the credentials, and we want to avoid
scripting.

This commit thus extends the infrastructure to implement a new backend,
which instead uses a simple configuration file containing key-value
pairs. The file follows the format which wpa_supplicant.conf(5) uses:
empty lines and comments are ignored, while passwords can be specified
with simple `password-name=password-value` assignments.

With this new backend, splitting up credentials and configuration
becomes trivial:

    # /etc/wpa_supplicant/wpa_supplicant.conf
    ext_password_backend=file:/etc/wpa_supplicant/psk.conf

    network={
        ssid="foobar"
        psk=ext:foobar
    }

    # /etc/wpa_supplicant/psk.conf
    foobar=ecdabff9c80632ec6fcffc4a8875e95d45cf93376d3b99da6881298853dc686b

Alternative approaches would be to support including other configuration
files in the main configuration, such that common configuration and
network declarations including credentials are split up into separate
files. But the implementation would probably have been more complex
compared to reusing the already-existing framework for external password
backends.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
src/utils/ext_password.c
src/utils/ext_password_file.c [new file with mode: 0644]
src/utils/ext_password_i.h
wpa_supplicant/Android.mk
wpa_supplicant/Makefile
wpa_supplicant/defconfig
wpa_supplicant/wpa_supplicant.conf

index 5615bd72a714d55032b60a29dec6a152fceb515a..cbf92de8c26d8115784f08a224d5fa62b715a338 100644 (file)
@@ -20,6 +20,9 @@ static const struct ext_password_backend *backends[] = {
 #ifdef CONFIG_EXT_PASSWORD_TEST
        &ext_password_test,
 #endif /* CONFIG_EXT_PASSWORD_TEST */
+#ifdef CONFIG_EXT_PASSWORD_FILE
+       &ext_password_file,
+#endif /* CONFIG_EXT_PASSWORD_FILE */
        NULL
 };
 
diff --git a/src/utils/ext_password_file.c b/src/utils/ext_password_file.c
new file mode 100644 (file)
index 0000000..4bb0095
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * External backend for file-backed passwords
+ * Copyright (c) 2021, Patrick Steinhardt <ps@pks.im>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/config.h"
+#include "ext_password_i.h"
+
+
+/**
+ * Data structure for the file-backed password backend.
+ */
+struct ext_password_file_data {
+       char *path; /* path of the password file */
+};
+
+
+/**
+ * ext_password_file_init - Initialize file-backed password backend
+ * @params: Parameters passed by the user.
+ * Returns: Pointer to the initialized backend.
+ *
+ * This function initializes a new file-backed password backend. The user is
+ * expected to initialize this backend with the parameters being the path of
+ * the file that contains the passwords.
+ */
+static void * ext_password_file_init(const char *params)
+{
+       struct ext_password_file_data *data;
+
+       if (!params) {
+               wpa_printf(MSG_ERROR, "EXT PW FILE: no path given");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (!data)
+               return NULL;
+
+       data->path = os_strdup(params);
+       if (!data->path) {
+               os_free(data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+/**
+ * ext_password_file_deinit - Deinitialize file-backed password backend
+ * @ctx: The file-backed password backend
+ *
+ * This function frees all data associated with the file-backed password
+ * backend.
+ */
+static void ext_password_file_deinit(void *ctx)
+{
+       struct ext_password_file_data *data = ctx;
+
+       str_clear_free(data->path);
+       os_free(data);
+}
+
+/**
+ * ext_password_file_get - Retrieve password from the file-backed password backend
+ * @ctx: The file-backed password backend
+ * @name: Name of the password to retrieve
+ * Returns: Buffer containing the password if one was found or %NULL.
+ *
+ * This function tries to find a password identified by name in the password
+ * file. The password is expected to be stored in `NAME=PASSWORD` format.
+ * Comments and empty lines in the file are ignored. Invalid lines will cause
+ * an error message, but will not cause the function to fail.
+ */
+static struct wpabuf * ext_password_file_get(void *ctx, const char *name)
+{
+       struct ext_password_file_data *data = ctx;
+       struct wpabuf *password = NULL;
+       char buf[512], *pos;
+       int line = 0;
+       FILE *f;
+
+       f = fopen(data->path, "r");
+       if (!f) {
+               wpa_printf(MSG_ERROR,
+                          "EXT PW FILE: could not open file '%s': %s",
+                          data->path, strerror(errno));
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EXT PW FILE: get(%s)", name);
+
+       while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
+               char *sep = os_strchr(pos, '=');
+
+               if (!sep) {
+                       wpa_printf(MSG_ERROR, "Invalid password line %d.",
+                                  line);
+                       continue;
+               }
+
+               if (!sep[1]) {
+                       wpa_printf(MSG_ERROR, "No password for line %d.", line);
+                       continue;
+
+               }
+
+               if (os_strncmp(name, pos, sep - pos) != 0)
+                       continue;
+
+               password = wpabuf_alloc_copy(sep + 1, os_strlen(sep + 1));
+               goto done;
+       }
+
+       wpa_printf(MSG_ERROR, "Password for '%s' was not found.", name);
+
+done:
+       forced_memzero(buf, sizeof(buf));
+       fclose(f);
+       return password;
+}
+
+
+const struct ext_password_backend ext_password_file = {
+       .name = "file",
+       .init = ext_password_file_init,
+       .deinit = ext_password_file_deinit,
+       .get = ext_password_file_get,
+};
index 948eaf5421b9e6a187041515cde3e518ab1f9a39..872ccd195b5a6797b6b0a958493e39ab2a066309 100644 (file)
@@ -26,4 +26,8 @@ struct wpabuf * ext_password_alloc(size_t len);
 extern const struct ext_password_backend ext_password_test;
 #endif /* CONFIG_EXT_PASSWORD_TEST */
 
+#ifdef CONFIG_EXT_PASSWORD_FILE
+extern const struct ext_password_backend ext_password_file;
+#endif /* CONFIG_EXT_PASSWORD_FILE */
+
 #endif /* EXT_PASSWORD_I_H */
index 5b551d4df0c81b7956b6c651fdbd229730faeab8..fd74c16409cca434db08edbde4738646afbf39f0 100644 (file)
@@ -1617,6 +1617,12 @@ L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST
 NEED_EXT_PASSWORD=y
 endif
 
+ifdef CONFIG_EXT_PASSWORD_FILE
+OBJS += src/utils/ext_password_file.c
+L_CFLAGS += -DCONFIG_EXT_PASSWORD_FILE
+NEED_EXT_PASSWORD=y
+endif
+
 ifdef NEED_EXT_PASSWORD
 OBJS += src/utils/ext_password.c
 L_CFLAGS += -DCONFIG_EXT_PASSWORD
index 5e9a696f6f2f569f3afea8ec4bcbad2726a2cace..bc3aa1d1c0a137e6fb6192bc446f3fcbe7dc2216 100644 (file)
@@ -1751,6 +1751,12 @@ CFLAGS += -DCONFIG_EXT_PASSWORD_TEST
 NEED_EXT_PASSWORD=y
 endif
 
+ifdef CONFIG_EXT_PASSWORD_FILE
+OBJS += ../src/utils/ext_password_file.o
+CFLAGS += -DCONFIG_EXT_PASSWORD_FILE
+NEED_EXT_PASSWORD=y
+endif
+
 ifdef NEED_EXT_PASSWORD
 OBJS += ../src/utils/ext_password.o
 CFLAGS += -DCONFIG_EXT_PASSWORD
index aaf64280f5052b253ee3425cc4ae4bb2ef18a4fb..708a82385170e997f614a5236f25f7e4d1d36fb9 100644 (file)
@@ -530,6 +530,8 @@ CONFIG_WIFI_DISPLAY=y
 #
 # External password backend for testing purposes (developer use)
 #CONFIG_EXT_PASSWORD_TEST=y
+# File-based backend to read passwords from an external file.
+#CONFIG_EXT_PASSWORD_FILE=y
 
 # Enable Fast Session Transfer (FST)
 #CONFIG_FST=y
index 9d5de859114ef35988064f183717168e44b4e46f..77f363e00aa6676dd75a5ebdfc8f602db7fb4b02 100644 (file)
@@ -366,7 +366,14 @@ fast_reauth=1
 
 # Password (and passphrase, etc.) backend for external storage
 # format: <backend name>[:<optional backend parameters>]
+# Test backend which stores passwords in memory. Should only be used for
+# development purposes.
 #ext_password_backend=test:pw1=password|pw2=testing
+# File-based backend which reads passwords from a file. The parameter
+# identifies the file to read passwords from. The password file follows the
+# format of wpa_supplicant.conf and accepts simple `key=passphrase` formatted
+# passwords.
+#ext_password_backend=file:/path/to/passwords.conf
 
 
 # Disable P2P functionality