[PATCH] test-runner: allow all unit tests to be run in VM
by James Prestwood
The -U parameter only allowed for a list of unit tests to be run.
Most of the time for sanity checking you want to run all the unit
tests so this has been changed to take an optional argument.
Now, the -U flag (by itself) will run all unit tests. Running a
single or list of unit tests can still be achieved by:
--unit-tests=test-eapol,test-crypto
---
tools/test-runner.c | 48 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 37 insertions(+), 11 deletions(-)
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 88cc16a8..b3e509c2 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -2364,28 +2364,54 @@ exit:
static void run_unit_tests(void)
{
- size_t i;
+ DIR *d;
+ struct dirent *dirent;
char *argv[2];
- char *unit_test_abs_path;
- char **unit_tests = l_strsplit(test_action_params, ',');
+ char *unit_test_abs_path = NULL;
+ char **unit_tests = NULL;
- if (!unit_tests || !unit_tests[0])
- goto exit;
+ if (strcmp(test_action_params, "")) {
+ unit_tests = l_strsplit(test_action_params, ',');
+
+ if (!unit_tests || !unit_tests[0])
+ goto exit;
+ }
if (chdir(top_level_path) < 0)
goto exit;
- for (i = 0; unit_tests[i]; i++) {
- unit_test_abs_path =
- l_strdup_printf("%s%s%s", top_level_path, "/unit/",
- unit_tests[i]);
+ d = opendir("unit/");
+ if (!d)
+ goto exit;
+
+ while ((dirent = readdir(d)) != NULL) {
+ struct stat st;
+
+ if (dirent->d_type != DT_REG)
+ continue;
+
+ unit_test_abs_path = l_strdup_printf("%s%s%s", top_level_path,
+ "/unit/", dirent->d_name);
+
+ if (stat(unit_test_abs_path, &st) < 0)
+ goto next;
+
+ if (!(st.st_mode & S_IEXEC))
+ goto next;
+
+ if (unit_tests) {
+ if (!l_strv_contains(unit_tests, dirent->d_name))
+ goto next;
+ }
argv[0] = unit_test_abs_path;
argv[1] = NULL;
+ l_info("\n---------- Unit %s ----------", dirent->d_name);
execute_program(argv, environ, true,
check_verbosity("unit"), false);
+next:
l_free(unit_test_abs_path);
}
@@ -2850,7 +2876,7 @@ static void usage(void)
static const struct option main_options[] = {
{ "auto-tests", required_argument, NULL, 'A' },
- { "unit-tests", required_argument, NULL, 'U' },
+ { "unit-tests", optional_argument, NULL, 'U' },
{ "qemu", required_argument, NULL, 'q' },
{ "kernel", required_argument, NULL, 'k' },
{ "verbose", required_argument, NULL, 'v' },
@@ -2887,7 +2913,7 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "A:U:q:k:v:g:Vdh", main_options,
+ opt = getopt_long(argc, argv, "A:q:k:v:g:UVdh", main_options,
NULL);
if (opt < 0)
break;
--
2.17.1
1 year, 4 months
[PATCH] station: Simplify and comply with coding style
by Tim Kourt
---
src/station.c | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/station.c b/src/station.c
index 9b3e087e..72393d51 100644
--- a/src/station.c
+++ b/src/station.c
@@ -1164,19 +1164,20 @@ static void station_enter_state(struct station *station,
case STATION_STATE_CONNECTED:
periodic_scan_stop(station);
+ if (!station->netconfig)
+ break;
+
if (station->state == STATION_STATE_ROAMING) {
- if (station->netconfig)
- netconfig_reconfigure(station->netconfig);
+ netconfig_reconfigure(station->netconfig);
break;
}
- if (station->netconfig)
- netconfig_configure(station->netconfig,
- network_get_settings(
- station->connected_network),
- netdev_get_address(
- station->netdev));
+ netconfig_configure(station->netconfig,
+ network_get_settings(
+ station->connected_network),
+ netdev_get_address(
+ station->netdev));
break;
case STATION_STATE_DISCONNECTING:
case STATION_STATE_ROAMING:
@@ -3066,9 +3067,10 @@ static void station_free(struct station *station)
if (station->connected_bss)
netdev_disconnect(station->netdev, NULL, NULL);
- if (station->netconfig)
+ if (station->netconfig) {
netconfig_destroy(station->netconfig);
- station->netconfig = NULL;
+ station->netconfig = NULL;
+ }
periodic_scan_stop(station);
--
2.13.6
1 year, 4 months
[RFC] test-runner: log process output to host system
by James Prestwood
All the processes verbose output is just written to stdout, and very
hard to parse after the test runs. Having test-runner write the
processes output to separate log files is much nicer to view after
the test runs.
A read/write file system is created which is separate and isolated
from the current FS mount (which mounts the whole host file system).
Doing this requires the user to create a folder somewhere to be used
as the mount point. This folder is where all the log files will end
up after test-runner runs.
Since test-runner must be run as root, care was taken to keep the
log files owned by the user which runs test-runner. The UID/GID
are passed to the VM, and any log files created are chown'ed back
to the user who ran test-runner.
execute_program was refactored, and both verbose and log arguments
were removed. The verbose argument was not really required since
we can do check_verbosity(argv[0]) internally. The 'log' flag was
only used along with --shell, and now the user can simply use
--log instead of dumping /tmp/iwd.log.
You can use this feature by specifying --log[=path] to test-runner.
If no path is provided the current directory will be used. Using
the --log flag will result in all processes being run with the
--verbose flag.
A new folder will be created under the --log path. The folder will
be of the name "run-<year>-<month>-<dat>-<PID>". Under this folder
you will find any global process logs. These are processes that
are only run once for ALL tests (hwsim, ifconfig, dbus etc.). There
will also be folders for specific tests that were run. Inside these
test folders will be logs of processes that are run per-test (iwd,
hostapd, python etc.).
---
tools/test-runner.c | 276 +++++++++++++++++++++++++++++++++++---------
1 file changed, 223 insertions(+), 53 deletions(-)
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 88cc16a8..2ba5e17c 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -45,6 +45,7 @@
#include <sys/reboot.h>
#include <sys/time.h>
#include <glob.h>
+#include <time.h>
#include <ell/ell.h>
#include "linux/nl80211.h"
@@ -82,6 +83,10 @@ const char *debug_filter;
static struct l_settings *hw_config;
static bool native_hw;
static bool shell;
+static bool log;
+static char log_dir[PATH_MAX];
+static uid_t log_uid;
+static gid_t log_gid;
static const char *qemu_binary;
static const char *kernel_image;
static const char *exec_home;
@@ -168,6 +173,14 @@ static bool check_verbosity(const char *app)
{
char **apps = verbose_apps;
+ /*
+ * All processes are verbose if logging is enabled. Kernel is a bit
+ * different as we just pipe dmesg into a log file at the end of
+ * execution.
+ */
+ if (log && strcmp(app, "kernel") != 0)
+ return true;
+
if (!apps)
return false;
@@ -358,6 +371,7 @@ static bool start_qemu(void)
int num_pci = 0, num_usb = 0;
char **pci_keys = NULL;
char **usb_keys = NULL;
+ L_AUTO_FREE_VAR(char *, log_option) = NULL;
has_virt = check_virtualization();
@@ -388,7 +402,8 @@ static bool start_qemu(void)
"TESTVERBOUT=\'%s\' DEBUG_FILTER=\'%s\'"
"TEST_ACTION=%u TEST_ACTION_PARAMS=\'%s\' "
"TESTARGS=\'%s\' PATH=\'%s\' VALGRIND=%u"
- "GDB=\'%s\' HW=\'%s\' SHELL=%u",
+ "GDB=\'%s\' HW=\'%s\' SHELL=%u "
+ "LOG_PATH=\'%s\' LOG_UID=\'%d\' LOG_GID=\'%d\'",
check_verbosity("kernel") ? "ignore_loglevel" : "quiet",
initcmd, cwd, verbose_opt ? verbose_opt : "none",
enable_debug ? debug_filter : "",
@@ -399,7 +414,9 @@ static bool start_qemu(void)
valgrind,
gdb_opt ? gdb_opt : "none",
hw_config ? "real" : "virtual",
- shell);
+ shell,
+ log ? log_dir : "none",
+ log_uid, log_gid);
if (hw_config) {
if (l_settings_has_group(hw_config, "PCIAdapters")) {
@@ -429,9 +446,10 @@ static bool start_qemu(void)
* -kernel,-append,-cpu,-host parameters (7)
* -enable-kvm and/or -usb (2)
* PCI and/or USB parameters (num_pci * 2) (num_usb * 2)
+ * Logging directory/device (2)
*/
argv = alloca(sizeof(qemu_argv) + sizeof(char *) *
- (7 + (2 + (num_pci * 2) + (num_usb * 2))));
+ (7 + (2 + (num_pci * 2) + (num_usb * 2) + 2)));
memcpy(argv, qemu_argv, sizeof(qemu_argv));
pos = (sizeof(qemu_argv) / sizeof(char *)) - 1;
@@ -480,6 +498,18 @@ static bool start_qemu(void)
}
}
+ if (log) {
+ /*
+ * Create a virtfs device and tag it. This allows the guest to
+ * mount 'logdir' in the path specified with --log.
+ */
+ log_option = l_strdup_printf("local,path=%s,mount_tag=logdir,"
+ "security_model=passthrough,id=logdir",
+ log_dir);
+ argv[pos++] = "-virtfs";
+ argv[pos++] = log_option;
+ }
+
argv[pos] = NULL;
execve(argv[0], argv, qemu_envp);
@@ -492,11 +522,12 @@ static bool start_qemu(void)
}
static pid_t execute_program(char *argv[], char *envp[], bool wait,
- bool verbose, bool log)
+ const char *test_name)
{
int status;
pid_t pid, child_pid;
char *str;
+ bool verbose;
if (!argv[0])
return -1;
@@ -512,18 +543,34 @@ static pid_t execute_program(char *argv[], char *envp[], bool wait,
}
if (child_pid == 0) {
- if (!verbose) {
- int fd;
- char log_file[PATH_MAX];
-
- if (!log)
- fd = open("/dev/null", O_WRONLY);
- else {
- sprintf(log_file, "/tmp/%s.log", argv[0]);
- fd = open(log_file, O_WRONLY | O_CREAT,
- S_IRUSR | S_IWUSR);
- }
+ int fd = -1;
+ char log_file[PATH_MAX];
+ verbose = check_verbosity(argv[0]);
+
+ /* No stdout and no logging */
+ if (!verbose && !log)
+ fd = open("/dev/null", O_WRONLY);
+ else if (log && verbose) {
+ /*
+ * Create the log file for this process. If no test name
+ * was specified this is a 'global' process (only run
+ * once, not per-test).
+ */
+ if (test_name) {
+ sprintf(log_file, "%s/%s/%s.log",
+ log_dir, test_name, argv[0]);
+ } else
+ sprintf(log_file, "%s/%s.log",
+ log_dir, argv[0]);
+
+ fd = open(log_file, O_WRONLY | O_CREAT | O_APPEND,
+ S_IRUSR | S_IWUSR);
+ if (fchown(fd, log_uid, log_gid) < 0)
+ l_error("fchown failed");
+ }
+
+ if (fd > -1) {
dup2(fd, 1);
dup2(fd, 2);
@@ -630,8 +677,7 @@ static bool start_dbus_daemon(void)
if (check_verbosity("dbus"))
setenv("DBUS_VERBOSE", "1", true);
- pid = execute_program(argv, environ, false,
- check_verbosity("dbus"), false);
+ pid = execute_program(argv, environ, false, NULL);
if (pid < 0)
return false;
@@ -642,7 +688,7 @@ static bool start_dbus_daemon(void)
argv[0] = "dbus-monitor";
argv[1] = "--system";
argv[2] = NULL;
- execute_program(argv, environ, false, true, false);
+ execute_program(argv, environ, false, NULL);
}
l_debug("D-Bus is running");
@@ -658,7 +704,7 @@ static bool start_haveged(void)
argv[0] = "haveged";
argv[1] = NULL;
- pid = execute_program(argv, environ, true, false, false);
+ pid = execute_program(argv, environ, true, NULL);
if (pid < 0)
return false;
@@ -680,7 +726,7 @@ static bool set_interface_state(const char *if_name, bool isUp)
argv[2] = state;
argv[3] = NULL;
- pid = execute_program(argv, environ, true, false, false);
+ pid = execute_program(argv, environ, true, NULL);
if (pid < 0)
return false;
@@ -702,7 +748,7 @@ static bool create_interface(const char *if_name, const char *phy_name)
argv[7] = "managed";
argv[8] = NULL;
- pid = execute_program(argv, environ, true, false, false);
+ pid = execute_program(argv, environ, true, NULL);
if (pid < 0)
return false;
@@ -720,7 +766,7 @@ static bool delete_interface(const char *if_name)
argv[3] = "del";
argv[4] = NULL;
- pid = execute_program(argv, environ, true, false, false);
+ pid = execute_program(argv, environ, true, NULL);
if (pid < 0)
return false;
@@ -736,7 +782,7 @@ static bool list_interfaces(void)
argv[1] = "-a";
argv[2] = NULL;
- pid = execute_program(argv, environ, true, true, false);
+ pid = execute_program(argv, environ, true, NULL);
if (pid < 0)
return false;
@@ -752,7 +798,7 @@ static bool list_hwsim_radios(void)
argv[1] = "--list";
argv[2] = NULL;
- pid = execute_program(argv, environ, true, true, false);
+ pid = execute_program(argv, environ, true, NULL);
if (pid < 0)
return false;
@@ -800,8 +846,7 @@ static int create_hwsim_radio(const char *radio_name,
argv[idx] = NULL;
- pid = execute_program(argv, environ, true,
- check_verbosity(BIN_HWSIM), false);
+ pid = execute_program(argv, environ, true, NULL);
if (pid < 0)
return -1;
@@ -820,7 +865,7 @@ static bool destroy_hwsim_radio(int radio_id)
argv[1] = destroy_param;
argv[2] = NULL;
- pid = execute_program(argv, environ, true, false, false);
+ pid = execute_program(argv, environ, true, NULL);
if (pid < 0)
return false;
@@ -840,8 +885,7 @@ static pid_t register_hwsim_as_trans_medium(void)
argv[idx++] = BIN_HWSIM;
argv[idx++] = NULL;
- return execute_program(argv, environ, false,
- check_verbosity(BIN_HWSIM), false);
+ return execute_program(argv, environ, false, NULL);
}
static void terminate_medium(pid_t medium_pid)
@@ -865,13 +909,13 @@ static void start_loopback(void)
argv[2] = "127.0.0.1";
argv[3] = "up";
argv[4] = NULL;
- execute_program(argv, environ, false, false, false);
+ execute_program(argv, environ, false, NULL);
argv[0] = "route";
argv[1] = "add";
argv[2] = "127.0.0.1";
argv[3] = NULL;
- execute_program(argv, environ, false, false, false);
+ execute_program(argv, environ, false, NULL);
loopback_started = true;
}
@@ -890,7 +934,7 @@ static pid_t start_phonesim(void)
setenv("OFONO_PHONESIM_CONFIG", "/tmp/phonesim.conf", true);
- return execute_program(argv, environ, false, false, false);
+ return execute_program(argv, environ, false, NULL);
}
static void stop_phonesim(pid_t pid)
@@ -916,7 +960,7 @@ static pid_t start_ofono(void)
start_loopback();
- return execute_program(argv, environ, false, verbose, false);
+ return execute_program(argv, environ, false, NULL);
}
static void stop_ofono(pid_t pid)
@@ -924,7 +968,8 @@ static void stop_ofono(pid_t pid)
kill_process(pid);
}
-static pid_t start_hostapd(char **config_files, struct wiphy **wiphys)
+static pid_t start_hostapd(char **config_files, struct wiphy **wiphys,
+ const char *test_name)
{
char **argv;
pid_t pid;
@@ -971,7 +1016,7 @@ static pid_t start_hostapd(char **config_files, struct wiphy **wiphys)
argv[idx++] = NULL;
}
- pid = execute_program(argv, environ, false, verbose, false);
+ pid = execute_program(argv, environ, false, log ? test_name : NULL);
if (pid < 0) {
goto exit;
}
@@ -1409,7 +1454,8 @@ hostapd_done:
(*phys_used)++;
}
- hostapd_pids_out[0] = start_hostapd(hostapd_config_file_paths, wiphys);
+ hostapd_pids_out[0] = start_hostapd(hostapd_config_file_paths, wiphys,
+ basename(config_dir_path));
hostapd_pids_out[1] = -1;
done:
@@ -1422,17 +1468,35 @@ done:
}
static pid_t start_iwd(const char *config_dir, struct l_queue *wiphy_list,
- const char *ext_options, int num_phys)
+ const char *ext_options, int num_phys, const char *test_name)
{
char *argv[13], **envp;
char *iwd_phys = NULL;
pid_t ret;
int idx = 0;
+ L_AUTO_FREE_VAR(char *, fd_option) = NULL;
+ char valgrind_log[PATH_MAX];
if (valgrind) {
+ int fd;
+
argv[idx++] = "valgrind";
argv[idx++] = "--leak-check=full";
- argv[idx++] = "--log-file=/tmp/valgrind.log";
+
+ /*
+ * Valgrind needs --log-fd if we want both stderr and stdout
+ */
+ if (log) {
+ snprintf(valgrind_log, sizeof(valgrind_log),
+ "%s/%s/valgrind.log", log_dir,
+ test_name);
+ fd = open(valgrind_log, O_WRONLY | O_CREAT | O_APPEND,
+ S_IRUSR | S_IWUSR);
+
+ fd_option = l_strdup_printf("--log-fd=%d", fd);
+ argv[idx++] = fd_option;
+ } else
+ argv[idx++] = "--log-file=/tmp/valgrind.log";
}
if (strcmp(gdb_opt, "iwd") == 0) {
@@ -1489,8 +1553,7 @@ static pid_t start_iwd(const char *config_dir, struct l_queue *wiphy_list,
envp = l_strv_append_printf(envp, "STATE_DIRECTORY=%s",
DAEMON_STORAGEDIR);
- ret = execute_program(argv, envp, false,
- check_verbosity(BIN_IWD), shell);
+ ret = execute_program(argv, envp, false, test_name);
l_strv_free(envp);
@@ -1714,7 +1777,8 @@ struct test_stats {
static void run_py_tests(struct l_settings *hw_settings,
struct l_queue *test_queue,
- struct l_queue *test_stats_queue)
+ struct l_queue *test_stats_queue,
+ const char *test_name)
{
char *argv[3];
pid_t test_exec_pid, test_timer_pid = -1;
@@ -1745,8 +1809,7 @@ start_next_test:
argv[2] = NULL;
print_test_status(py_test, TEST_STATUS_STARTED, 0);
- test_exec_pid = execute_program(argv, environ, false,
- check_verbosity("pytests"), false);
+ test_exec_pid = execute_program(argv, environ, false, test_name);
gettimeofday(&time_before, NULL);
@@ -1887,7 +1950,7 @@ static void set_reg_domain(const char *domain)
argv[3] = (char *) domain;
argv[4] = NULL;
- execute_program(argv, environ, false, false, false);
+ execute_program(argv, environ, false, NULL);
}
static void wiphy_up(void *data, void *user_data)
@@ -1931,6 +1994,7 @@ static void create_network_and_run_tests(void *data, void *user_data)
int phys_used;
int num_radios;
struct test_entry *entry = data;
+ char *test_name = NULL;
memset(hostapd_pids, -1, sizeof(hostapd_pids));
@@ -1952,6 +2016,19 @@ static void create_network_and_run_tests(void *data, void *user_data)
l_info("Configuring network...");
+ if (log) {
+ char *log_path;
+
+ test_name = basename(config_dir_path);
+ log_path = l_strdup_printf("%s/%s", log_dir, test_name);
+
+ mkdir(log_path, 0755);
+ if (chown(log_path, log_uid, log_gid) < 0)
+ l_error("chown failed");
+
+ l_free(log_path);
+ }
+
if (chdir(config_dir_path) < 0) {
l_error("Failed to change to test directory: %s",
strerror(errno));
@@ -2076,7 +2153,7 @@ static void create_network_and_run_tests(void *data, void *user_data)
iwd_config_dir = DAEMON_CONFIGDIR;
iwd_pid = start_iwd(iwd_config_dir, wiphy_list,
- iwd_ext_options, iwd_phys);
+ iwd_ext_options, iwd_phys, test_name);
if (iwd_pid == -1)
goto exit_hostapd;
@@ -2094,7 +2171,8 @@ static void create_network_and_run_tests(void *data, void *user_data)
set_wiphy_list(wiphy_list);
if (!shell)
- run_py_tests(hw_settings, test_queue, test_stats_queue);
+ run_py_tests(hw_settings, test_queue, test_stats_queue,
+ test_name);
else {
if (system("/bin/sh"))
l_info("executing /bin/sh failed");
@@ -2106,10 +2184,23 @@ static void create_network_and_run_tests(void *data, void *user_data)
if (iwd_pid > 0)
terminate_iwd(iwd_pid);
- if (valgrind)
+ if (valgrind && !log)
if (system("cat /tmp/valgrind.log"))
l_info("cat /tmp/valgrind.log failed");
+ if (log) {
+ L_AUTO_FREE_VAR(char *, dmesg);
+ L_AUTO_FREE_VAR(char *, kernel_log);
+
+ kernel_log = l_strdup_printf("%s/kernel.log", log_dir);
+ dmesg = l_strdup_printf("dmesg > %s", kernel_log);
+
+ if (system(dmesg))
+ l_error("dmesg failed");
+ if (chown(kernel_log, log_uid, log_gid))
+ l_error("chown failed");
+ }
+
if (ofono_req) {
loopback_started = false;
stop_ofono(ofono_pid);
@@ -2252,13 +2343,21 @@ static void free_test_entry(void *data)
static void run_auto_tests(void)
{
- L_AUTO_FREE_VAR(char*, test_home_path);
- L_AUTO_FREE_VAR(char*, env_path);
+ L_AUTO_FREE_VAR(char*, test_home_path) = NULL;
+ L_AUTO_FREE_VAR(char*, env_path) = NULL;
int i;
struct l_queue *test_config_queue;
struct l_queue *test_stat_queue;
char **test_config_dirs;
+ if (log) {
+ if (mount("logdir", log_dir, "9p", 0,
+ "trans=virtio,version=9p2000.L") < 0) {
+ l_error("Mounting %s failed", log_dir);
+ return;
+ }
+ }
+
env_path = l_strdup_printf("%s/src:%s/tools:%s", top_level_path,
top_level_path, getenv("PATH"));
@@ -2383,8 +2482,7 @@ static void run_unit_tests(void)
argv[0] = unit_test_abs_path;
argv[1] = NULL;
- execute_program(argv, environ, true,
- check_verbosity("unit"), false);
+ execute_program(argv, environ, true, NULL);
l_free(unit_test_abs_path);
}
@@ -2632,6 +2730,38 @@ static void run_tests(void)
return;
}
+ ptr = strstr(cmdline, "LOG_GID=");
+ if (ptr) {
+ *ptr = '\0';
+ test_action_str = ptr + 9;
+ ptr = strchr(test_action_str, '\'');
+ *ptr = '\0';
+ log_gid = atoi(test_action_str);
+ }
+
+ ptr = strstr(cmdline, "LOG_UID=");
+ if (ptr) {
+ *ptr = '\0';
+ test_action_str = ptr + 9;
+ ptr = strchr(test_action_str, '\'');
+ *ptr = '\0';
+ log_uid = atoi(test_action_str);
+ }
+
+ ptr = strstr(cmdline, "LOG_PATH=");
+ if (ptr) {
+ *ptr = '\0';
+ test_action_str = ptr + 10;
+
+ ptr = strchr(test_action_str, '\'');
+ *ptr = '\0';
+
+ if (strcmp(test_action_str, "none")) {
+ log = true;
+ strcpy(log_dir, test_action_str);
+ }
+ }
+
ptr = strstr(cmdline, "SHELL=");
if (ptr) {
*ptr = '\0';
@@ -2839,7 +2969,13 @@ static void usage(void)
"\t\t\t\tbut no test will be run. If no"
" test is specified\n"
"\t\t\t\tthe 'shell' test"
- " will be used");
+ " will be used\n"
+ "\t-l, --log [dir] Directory used for log output. "
+ "If no directory\n"
+ "\t\t\t\tis specified the "
+ "current directory will be used\n"
+ "\t\t\t\tThis option sets --verbose on "
+ "all apps");
l_info("Commands:\n"
"\t-A, --auto-tests <dirs> Comma separated list of the "
"test configuration\n\t\t\t\t"
@@ -2859,6 +2995,7 @@ static const struct option main_options[] = {
{ "valgrind", no_argument, NULL, 'V' },
{ "hw", required_argument, NULL, 'w' },
{ "shell", optional_argument, NULL, 's' },
+ { "log", optional_argument, NULL, 'l' },
{ "help", no_argument, NULL, 'h' },
{ }
};
@@ -2866,6 +3003,8 @@ static const struct option main_options[] = {
int main(int argc, char *argv[])
{
uint8_t actions = 0;
+ struct tm *timeinfo;
+ time_t t;
l_log_set_stderr();
@@ -2887,7 +3026,7 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "A:U:q:k:v:g:Vdh", main_options,
+ opt = getopt_long(argc, argv, "A:U:q:k:v:g:Vldh", main_options,
NULL);
if (opt < 0)
break;
@@ -2947,6 +3086,37 @@ int main(int argc, char *argv[])
break;
case 's':
shell = true;
+ break;
+ case 'l':
+ /*
+ * Setup the log directory. This is created under the
+ * passed in log dir (--log) in the format:
+ * <logdir>/run-<year>-<month>-<day>-<PID>
+ *
+ * The created log dir is then chown'ed to the user
+ * who started test-runner, as are all files created
+ * under this directory.
+ */
+ log = true;
+
+ if (!optarg)
+ optarg = ".";
+
+ time(&t);
+ timeinfo = localtime(&t);
+
+ log_gid = atoi(getenv("SUDO_GID"));
+ log_uid = atoi(getenv("SUDO_UID"));
+
+ snprintf(log_dir, sizeof(log_dir), "%s/run-%d-%d-%d-%d",
+ optarg, timeinfo->tm_year,
+ timeinfo->tm_mon, timeinfo->tm_mday,
+ getpid());
+ mkdir(log_dir, 0755);
+
+ if (chown(log_dir, log_uid, log_gid) < 0)
+ l_error("fchown failed");
+
break;
case 'h':
usage();
--
2.17.1
1 year, 4 months
[PATCH] auto-t: add TLS test with embedded PEMs
by James Prestwood
---
.../connection_test.py | 77 +++++++++++++++
autotests/testEAP-TLS-embedded-pems/hw.conf | 6 ++
.../ssidEAP-TLS.8021x | 94 +++++++++++++++++++
.../ssidEAP-TLS.conf | 12 +++
4 files changed, 189 insertions(+)
create mode 100644 autotests/testEAP-TLS-embedded-pems/connection_test.py
create mode 100644 autotests/testEAP-TLS-embedded-pems/hw.conf
create mode 100644 autotests/testEAP-TLS-embedded-pems/ssidEAP-TLS.8021x
create mode 100644 autotests/testEAP-TLS-embedded-pems/ssidEAP-TLS.conf
diff --git a/autotests/testEAP-TLS-embedded-pems/connection_test.py b/autotests/testEAP-TLS-embedded-pems/connection_test.py
new file mode 100644
index 00000000..1102b810
--- /dev/null
+++ b/autotests/testEAP-TLS-embedded-pems/connection_test.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python3
+
+import unittest
+import sys
+
+sys.path.append('../util')
+import iwd
+from iwd import IWD
+from iwd import PSKAgent
+from iwd import NetworkType
+import testutil
+import hostapd
+
+class Test(unittest.TestCase):
+
+ def do_test_connection_success(self, ssid, passphrase=None):
+ wd = IWD()
+
+ if passphrase:
+ psk_agent = PSKAgent(passphrase)
+ wd.register_psk_agent(psk_agent)
+
+ hostapd_ifname = None
+ for ifname in hostapd.hostapd_map:
+ if ssid + '.conf' in hostapd.hostapd_map[ifname].config:
+ hostapd_ifname = ifname
+ break
+
+ devices = wd.list_devices(1)
+ device = devices[0]
+
+ condition = 'not obj.scanning'
+ wd.wait_for_object_condition(device, condition)
+
+ if not device.get_ordered_networks():
+ device.scan()
+ condition = 'obj.scanning'
+ wd.wait_for_object_condition(device, condition)
+ condition = 'not obj.scanning'
+ wd.wait_for_object_condition(device, condition)
+
+ ordered_network = device.get_ordered_network(ssid)
+
+ self.assertEqual(ordered_network.type, NetworkType.eap)
+
+ condition = 'not obj.connected'
+ wd.wait_for_object_condition(ordered_network.network_object, condition)
+
+ ordered_network.network_object.connect()
+
+ condition = 'obj.connected'
+ wd.wait_for_object_condition(ordered_network.network_object, condition)
+
+ testutil.test_iface_operstate()
+ testutil.test_ifaces_connected(hostapd_ifname, device.name)
+
+ device.disconnect()
+
+ condition = 'not obj.connected'
+ wd.wait_for_object_condition(ordered_network.network_object, condition)
+
+ if passphrase:
+ wd.unregister_psk_agent(psk_agent)
+
+ def test_eap_tls(self):
+ self.do_test_connection_success('ssidEAP-TLS')
+
+ @classmethod
+ def setUpClass(cls):
+ IWD.copy_to_storage('ssidEAP-TLS.8021x')
+
+ @classmethod
+ def tearDownClass(cls):
+ IWD.clear_storage()
+
+if __name__ == '__main__':
+ unittest.main(exit=True)
diff --git a/autotests/testEAP-TLS-embedded-pems/hw.conf b/autotests/testEAP-TLS-embedded-pems/hw.conf
new file mode 100644
index 00000000..0cbda377
--- /dev/null
+++ b/autotests/testEAP-TLS-embedded-pems/hw.conf
@@ -0,0 +1,6 @@
+[SETUP]
+num_radios=2
+tmpfs_extra_stuff=../misc/certs
+
+[HOSTAPD]
+rad0=ssidEAP-TLS.conf
diff --git a/autotests/testEAP-TLS-embedded-pems/ssidEAP-TLS.8021x b/autotests/testEAP-TLS-embedded-pems/ssidEAP-TLS.8021x
new file mode 100644
index 00000000..e1c34e46
--- /dev/null
+++ b/autotests/testEAP-TLS-embedded-pems/ssidEAP-TLS.8021x
@@ -0,0 +1,94 @@
+[Security]
+EAP-Method=TLS
+EAP-TLS-CACert=embed:cert_ca
+EAP-TLS-ClientCert=embed:cert_client
+EAP-TLS-ClientKey=embed:cert_client_key
+EAP-Identity=abc(a)example.com
+
+[@pem@cert_ca]
+-----BEGIN CERTIFICATE-----
+MIIEVDCCAzygAwIBAgIJAJmt2W7CutHvMA0GCSqGSIb3DQEBCwUAMHgxNTAzBgNV
+BAoMLEludGVybmF0aW9uYWwgVW5pb24gb2YgRXhhbXBsZSBPcmdhbml6YXRpb25z
+MR8wHQYDVQQDDBZDZXJ0aWZpY2F0ZSBpc3N1ZXIgZ3V5MR4wHAYJKoZIhvcNAQkB
+Fg9jYUBtYWlsLmV4YW1wbGUwHhcNMTYwNTE3MjEyMDQ2WhcNNDMxMDAzMjEyMDQ2
+WjB4MTUwMwYDVQQKDCxJbnRlcm5hdGlvbmFsIFVuaW9uIG9mIEV4YW1wbGUgT3Jn
+YW5pemF0aW9uczEfMB0GA1UEAwwWQ2VydGlmaWNhdGUgaXNzdWVyIGd1eTEeMBwG
+CSqGSIb3DQEJARYPY2FAbWFpbC5leGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAo3GrGqW49h8kY2Wx/1kd5dIkYGazuWrX93ma9904hHBJNsvu
+V34QfHVln6wDpMZMwuvkfct09kl0rQpztJzA9YL4GMdmV6+6J6LiX1kMqLkNaJa+
+Ov+ECG5ypBRbSTYKpqFsc5wPOQf/N8brBiZS1v67va3fCwO6dgLeAf7dZ3Q70oUr
+mghbK8UnlC+wLShxCBAW8TUKg7B7M5Gea794CO9wH7NsFyAr963WVcLxrdL3xMHZ
+9hcscrljh35nCAc6sum1cTtWI651OGehr0Bhp2o2Exgr2mbo5TobqEW+fe4gc4ik
+0nzHGWiOVaszUcvpeeduGV3y6om93atffeKuxQIDAQABo4HgMIHdMA8GA1UdEwQI
+MAYBAf8CAQAwHQYDVR0OBBYEFO+M3tJAELTnseUqZyP4vl5X7SmUMIGqBgNVHSME
+gaIwgZ+AFO+M3tJAELTnseUqZyP4vl5X7SmUoXykejB4MTUwMwYDVQQKDCxJbnRl
+cm5hdGlvbmFsIFVuaW9uIG9mIEV4YW1wbGUgT3JnYW5pemF0aW9uczEfMB0GA1UE
+AwwWQ2VydGlmaWNhdGUgaXNzdWVyIGd1eTEeMBwGCSqGSIb3DQEJARYPY2FAbWFp
+bC5leGFtcGxlggkAma3ZbsK60e8wDQYJKoZIhvcNAQELBQADggEBAA/Yb9jB94OF
+swbyCrA6Qe53YGC4dfqrKGRThtGKTrH0XcM2x2qLIIbiNDogwhRqlUW8iNY6Dm2k
+43mJzNsYhy7Nt3IJFCguTJFilfGzQnBtK8wCr/C9qsj//BESOIlo/TDZ2Ho4ixcJ
+n+FTnN34F6JJ0DIvA6tNBe1kUFSrbubL8ygNWJ9BKMebEzokGNGCGFNr70DlQj2o
+1EOMMOkj0gWO0WegAYFLojzag3l+uvU59YE+/fbZ2iclyvbF7IutQ5M9g5TnQE6F
+f+qFKR5+bhlJwry6vLl/6ulihkvF3y1bm7zae62zbFaZRU6PJUl1DtXiA23ZTm9T
+VDivqs07R84=
+-----END CERTIFICATE-----
+
+[@pem@cert_client]
+-----BEGIN CERTIFICATE-----
+MIIEPTCCAyWgAwIBAgIJAPk7rut4SWQCMA0GCSqGSIb3DQEBCwUAMHgxNTAzBgNV
+BAoMLEludGVybmF0aW9uYWwgVW5pb24gb2YgRXhhbXBsZSBPcmdhbml6YXRpb25z
+MR8wHQYDVQQDDBZDZXJ0aWZpY2F0ZSBpc3N1ZXIgZ3V5MR4wHAYJKoZIhvcNAQkB
+Fg9jYUBtYWlsLmV4YW1wbGUwHhcNMTYwNTE3MjEyMDQ3WhcNNDMxMDAzMjEyMDQ3
+WjBnMSEwHwYDVQQKDBhCYXIgRXhhbXBsZSBPcmdhbml6YXRpb24xITAfBgNVBAMM
+GEJhciBFeGFtcGxlIE9yZ2FuaXphdGlvbjEfMB0GCSqGSIb3DQEJARYQYmFyQG1h
+aWwuZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOE5D/lU
+haTC3xL281ttZPRURXlKJqLwcHGXQSuQm6wwYWtAhLyMLEHrryE0oChKdw3eV7Nn
+/IODxvk1S8uIuKfHWuNd5qX/yu7CjCWvyim2CSJHF24rQFmb9ePoddOZnDMMAIz7
+PC325JVhbr/LSBLbqhZ0smHy1HKyrzzHHzKU4YcTH/3+3H4MHZwnNZfbfG5qhRZG
+Nuu/8t+AWVcEocPRGYZpzWJNq6AAzojAHSSOxxiscBMiuQ+BdofPw9XhwpS+Fstk
+rvF8J9FfZj5U3FOm/EgOQn8efnrUL231PqB1R9PIKYv/938p3iDMIi0ETiKi5ced
+WV8m2PcykPdNOKMCAwEAAaOB2jCB1zAJBgNVHRMEAjAAMB0GA1UdDgQWBBTs9eey
+OkMw3uiPpDOa3b9KErbEfzCBqgYDVR0jBIGiMIGfgBTvjN7SQBC057HlKmcj+L5e
+V+0plKF8pHoweDE1MDMGA1UECgwsSW50ZXJuYXRpb25hbCBVbmlvbiBvZiBFeGFt
+cGxlIE9yZ2FuaXphdGlvbnMxHzAdBgNVBAMMFkNlcnRpZmljYXRlIGlzc3VlciBn
+dXkxHjAcBgkqhkiG9w0BCQEWD2NhQG1haWwuZXhhbXBsZYIJAJmt2W7CutHvMA0G
+CSqGSIb3DQEBCwUAA4IBAQA8MxPjU2h5gwntQeSs8eeaEUILMkoU6JSDS4s5Hex5
+xYMLfcSoPPI0E6ahvKtWkSM0UZThyWsulSDTI1EgAiebjms06m1Ogh9V+0VbcOlQ
+D/k3+fSRIiyY+v3J/h8ArUby+m5O2g1TgECr/nZl4avoAI0RpBi3lH6tC8GQYdbc
+SA6hpNCM/dY3LWtAo2W6mdE8+RlCuTj4VZiQ1g6GE77t6XwDFL6vQBzLLXrinvXK
+Ha+IssV5sGdpH9bVFWIJV2q3OZuv3HLhQfGmeUrGyWVcokQQ8d6kRwg65Zb1+KT2
+bNlVKhPAMBk4ayEocpqFIfqfCKDjGdPUruIh8IVDc684
+-----END CERTIFICATE-----
+
+[@pem@cert_client_key]
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDhOQ/5VIWkwt8S
+9vNbbWT0VEV5Siai8HBxl0ErkJusMGFrQIS8jCxB668hNKAoSncN3lezZ/yDg8b5
+NUvLiLinx1rjXeal/8ruwowlr8optgkiRxduK0BZm/Xj6HXTmZwzDACM+zwt9uSV
+YW6/y0gS26oWdLJh8tRysq88xx8ylOGHEx/9/tx+DB2cJzWX23xuaoUWRjbrv/Lf
+gFlXBKHD0RmGac1iTaugAM6IwB0kjscYrHATIrkPgXaHz8PV4cKUvhbLZK7xfCfR
+X2Y+VNxTpvxIDkJ/Hn561C9t9T6gdUfTyCmL//d/Kd4gzCItBE4iouXHnVlfJtj3
+MpD3TTijAgMBAAECggEBAIbg9YAL7j1NtupUmkkWqm7oSPLqRVkvRSfBvXWplJD6
+KF1itht0lsyjqK3qJj/62HGlxj/a9o6MTIzSLiImLu/Lo9KmWYrwNUfnmqa3MArq
+yW2NxapknJUNoaRrgqTGSZUIiwvjKZcdVKdhQkH6K5+fja0FFg8yrahC+k8bsMNI
+5mw8NwRdR3SvHJWHCLfKCQ31tju7On/4C6jr0siUCc2//W+SO5c+FHDY1bma02cp
+jXTEiFpw91YcyKxiADIaH9/qfxWdefxqYg1WlUeXF3jYt5xYnYr34qKW1gOZ3jy1
+QJ3esn382ZTml3TFZWy+g9tkYyOSgmDwQZbLk/ppBAECgYEA8RzLBFwP018ieMBv
+khDtwcKk6ZihkWZxEPQPuUljWzzAHn/f3dXOcrfmflAKeoDEeYDimDYDizTLDPC4
+zmWkMJHNadcM5H065BbGVFQWXo47ltccfIlB/1vzG8aywfJ/yNfHvH87wbH2eg6N
+yOr+96ZjLJszQ+Rv189BbXDzTcMCgYEA7yEbUL/A1J0l2kLoYyS0vfVa7AyBVOFW
+vPgfkF7HdNpIiFWlukMr+DWOolaoZp5iHqQXFwJsL8qCcrbZuHbaNHAI/5vDE9xG
+fh8KzrfBrjIPIyNm6EWpsBo5unXK+wTeqIAGKdzDo5Q3zEE6G5DkkHItKA7yjPOM
+gz/b/MR3W6ECgYBBv3dA3hXWrreIs/j4nLMoxfoQVPWh34xvcg4jmXaFd6Bv8LDM
+HjRopestgIgK9bgd5d5kYT5AJIpGIhJS/fZy5B9egCzc1aVMc0Vr024yJJjtPgVf
+lFIx3xIA/gLazlS4INcveIaEABJVIEjbg/E4+N9MV5n4Jn+1GqgdvtIp3wKBgQC0
+C3lFkxrc+nVFoJrYCwsK+3E5yTCXeBKWtTsOuE307WUvQU1GsMyqVajPEfA5U4cN
+Cv9Xk7thQFh3hrTm7pXcZX5g9iYrDe8FhtncSv7I6Wf8TOtudwUMUrKkcYwi88ex
+lrMNUer7ft2ELJhTqQRuvYjCYH6/IaDqMWqxJju4AQKBgQDPjOh75ykQc93SsYpt
+Tb4gQKLeqOb57pofT8D44DccatfEgk31D4fBIIQu6XKopQmCtQyX9DUDjOWFTxuo
+IMPysN6Fh1quCbC6Xt5xfKoaJG5yQYKeKtLhknwEW9SUifU2xVrOcPikLs7Iwmmp
+BkDLsu/YKwRFSfrbYZXbTlU8tQ==
+-----END PRIVATE KEY-----
+
+[Settings]
+Autoconnect=False
diff --git a/autotests/testEAP-TLS-embedded-pems/ssidEAP-TLS.conf b/autotests/testEAP-TLS-embedded-pems/ssidEAP-TLS.conf
new file mode 100644
index 00000000..e1d7c6b4
--- /dev/null
+++ b/autotests/testEAP-TLS-embedded-pems/ssidEAP-TLS.conf
@@ -0,0 +1,12 @@
+hw_mode=g
+channel=1
+ssid=ssidEAP-TLS
+
+wpa=3
+wpa_key_mgmt=WPA-EAP
+ieee8021x=1
+eap_server=1
+eap_user_file=/tmp/certs/eap-user-tls.text
+ca_cert=/tmp/certs/cert-ca.pem
+server_cert=/tmp/certs/cert-server.pem
+private_key=/tmp/certs/cert-server-key.pem
--
2.17.1
1 year, 4 months
[PATCH v2] tools: ios_convert: embed certs rather than using paths
by James Prestwood
---
tools/ios_convert.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
-v2:
* removed extra new line after the embedded PEM
diff --git a/tools/ios_convert.py b/tools/ios_convert.py
index 8aa10113..29a395ba 100755
--- a/tools/ios_convert.py
+++ b/tools/ios_convert.py
@@ -114,7 +114,7 @@ def write_network(network, root_ca_path):
output += "EAP-Identity=anonymous\n"
if root_ca_path:
- output += "EAP-%s-CACert=%s\n" % (eap, root_ca_path)
+ output += "EAP-%s-CACert=embed:root_ca\n" % eap
output += "EAP-%s-Phase2-Method=Tunneled-%s\n" % \
(eap, network.inner_eap)
@@ -158,6 +158,11 @@ def write_network(network, root_ca_path):
output += "\n"
+ if root_ca_path:
+ output += "[@pem@root_ca]\n"
+ with open(root_ca_path) as f:
+ output += f.read()
+
print("Provisioning network %s\n" % conf_file)
if args.verbose:
--
2.17.1
1 year, 4 months
[PATCH] tools: ios_convert: embed certs rather than using paths
by James Prestwood
---
tools/ios_convert.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tools/ios_convert.py b/tools/ios_convert.py
index 8aa10113..d3b6cc8d 100755
--- a/tools/ios_convert.py
+++ b/tools/ios_convert.py
@@ -114,7 +114,7 @@ def write_network(network, root_ca_path):
output += "EAP-Identity=anonymous\n"
if root_ca_path:
- output += "EAP-%s-CACert=%s\n" % (eap, root_ca_path)
+ output += "EAP-%s-CACert=embed:root_ca\n" % eap
output += "EAP-%s-Phase2-Method=Tunneled-%s\n" % \
(eap, network.inner_eap)
@@ -158,6 +158,12 @@ def write_network(network, root_ca_path):
output += "\n"
+ if root_ca_path:
+ output += "[@pem@root_ca]\n"
+ with open(root_ca_path) as f:
+ output += f.read()
+ output += '\n'
+
print("Provisioning network %s\n" % conf_file)
if args.verbose:
--
2.17.1
1 year, 4 months
[PATCH v3 1/3] eap-tls-common: allow embedded PEMs in settings
by James Prestwood
Refactoring was required to allow for embedded certs. The existing
eap_tls_state object was changed to hold the cert types (l_queue,
l_certchain, l_key) rather than the file path, since there may not
actually be separate PEM files.
Care was taken to properly manage the memory of these objects.
Since the TLS object takes ownership when setting auth data or the
CA certs all error cases must be handled properly to free these
objects after they are loaded and in addition they must be set to
NULL so that the cleanup doesn't double free them.
If everything goes to plan, we load all the PEMs in settings_load,
provide these objects to the TLS APIs, and then NULL out the
pointers (TLS now owns this memory). If anything fails between
settings_load and l_tls_start we must free these objects.
A special format must be used to indicate that a PEM is embedded
inside the settings file. First, the l_settings format should be
followed for the PEM itself, e.g.
[@pem@my_ca_cert]
<CA Cert data>
This PEM can then be referenced by "embed:my_ca_cert", e.g.
EAP-TLS-CACert=embed:my_ca_cert
Any other value not starting with "embed:" will be treated as a file
path.
---
src/eap-tls-common.c | 259 +++++++++++++++++++++++++++++--------------
1 file changed, 177 insertions(+), 82 deletions(-)
diff --git a/src/eap-tls-common.c b/src/eap-tls-common.c
index 39015167..b76770ce 100644
--- a/src/eap-tls-common.c
+++ b/src/eap-tls-common.c
@@ -113,10 +113,9 @@ struct eap_tls_state {
bool expecting_frag_ack:1;
- char *ca_cert;
- char *client_cert;
- char *client_key;
- char *passphrase;
+ struct l_queue *ca_cert;
+ struct l_certchain *client_cert;
+ struct l_key *client_key;
char **domain_mask;
const struct eap_tls_variant_ops *variant_ops;
@@ -154,6 +153,21 @@ static void __eap_tls_common_state_reset(struct eap_tls_state *eap_tls)
}
}
+static void __eap_tls_common_state_free(struct eap_tls_state *eap_tls)
+{
+ if (eap_tls->ca_cert)
+ l_queue_destroy(eap_tls->ca_cert,
+ (l_queue_destroy_func_t)l_cert_free);
+ if (eap_tls->client_cert)
+ l_certchain_free(eap_tls->client_cert);
+
+ if (eap_tls->client_key)
+ l_key_free(eap_tls->client_key);
+
+ l_strv_free(eap_tls->domain_mask);
+ l_free(eap_tls);
+}
+
bool eap_tls_common_state_reset(struct eap_state *eap)
{
struct eap_tls_state *eap_tls = eap_get_data(eap);
@@ -177,18 +191,7 @@ void eap_tls_common_state_free(struct eap_state *eap)
if (eap_tls->variant_ops->destroy)
eap_tls->variant_ops->destroy(eap_tls->variant_data);
- l_free(eap_tls->ca_cert);
- l_free(eap_tls->client_cert);
- l_free(eap_tls->client_key);
-
- if (eap_tls->passphrase) {
- explicit_bzero(eap_tls->passphrase,
- strlen(eap_tls->passphrase));
- l_free(eap_tls->passphrase);
- }
-
- l_strv_free(eap_tls->domain_mask);
- l_free(eap_tls);
+ __eap_tls_common_state_free(eap_tls);
}
static void eap_tls_tunnel_debug(const char *str, void *user_data)
@@ -544,55 +547,34 @@ static bool eap_tls_tunnel_init(struct eap_state *eap)
NULL);
if (eap_tls->client_cert || eap_tls->client_key) {
- struct l_certchain *client_cert =
- l_pem_load_certificate_chain(eap_tls->client_cert);
- struct l_key *client_key;
-
- if (!client_cert) {
- l_error("%s: Failed to parse client certificate: %s.",
- eap_get_method_name(eap),
- eap_tls->client_cert);
- return false;
- }
+ if (!l_tls_set_auth_data(eap_tls->tunnel, eap_tls->client_cert,
+ eap_tls->client_key)) {
+ l_certchain_free(eap_tls->client_cert);
+ eap_tls->client_cert = NULL;
- client_key = l_pem_load_private_key(eap_tls->client_key,
- eap_tls->passphrase,
- NULL);
- if (!client_key) {
- l_error("%s: Failed to parse client private key: %s.",
- eap_get_method_name(eap),
- eap_tls->client_key);
- return false;
- }
+ l_key_free(eap_tls->client_key);
+ eap_tls->client_key = NULL;
- if (!l_tls_set_auth_data(eap_tls->tunnel,
- client_cert, client_key)) {
- l_certchain_free(client_cert);
- l_key_free(client_key);
l_error("%s: Failed to set auth data.",
eap_get_method_name(eap));
return false;
}
+
+ eap_tls->client_cert = NULL;
+ eap_tls->client_key = NULL;
}
if (eap_tls->ca_cert) {
- struct l_queue *ca_cert =
- l_pem_load_certificate_list(eap_tls->ca_cert);
-
- if (!ca_cert) {
- l_error("%s: Error loading CA certificates from %s.",
- eap_get_method_name(eap),
- eap_tls->ca_cert);
- return false;
- }
-
- if (!l_tls_set_cacert(eap_tls->tunnel, ca_cert)) {
- l_queue_destroy(ca_cert,
+ if (!l_tls_set_cacert(eap_tls->tunnel, eap_tls->ca_cert)) {
+ l_queue_destroy(eap_tls->ca_cert,
(l_queue_destroy_func_t)l_cert_free);
+ eap_tls->ca_cert = NULL;
l_error("%s: Error settings CA certificates.",
eap_get_method_name(eap));
return false;
}
+
+ eap_tls->ca_cert = NULL;
}
if (eap_tls->domain_mask)
@@ -793,6 +775,83 @@ error:
eap_method_error(eap);
}
+static const char *load_embedded_pem(struct l_settings *settings,
+ const char *name)
+{
+ const char *pem;
+ const char *type;
+
+ pem = l_settings_get_embedded_value(settings, name + 6, &type);
+ if (!pem)
+ return NULL;
+
+ if (strcmp(type, "pem"))
+ return NULL;
+
+ return pem;
+}
+
+static bool is_embedded(const char *str)
+{
+ if (!str)
+ return false;
+
+ if (strlen(str) < 6)
+ return false;
+
+ if (!strncmp("embed:", str, 6))
+ return true;
+
+ return false;
+}
+
+static struct l_queue *eap_tls_load_ca_cert(struct l_settings *settings,
+ const char *value)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_certificate_list(value);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_certificate_list_from_data(pem, strlen(pem));
+}
+
+static struct l_certchain *eap_tls_load_client_cert(struct l_settings *settings,
+ const char *value)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_certificate_chain(value);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_certificate_chain_from_data(pem, strlen(pem));
+}
+
+static struct l_key *eap_tls_load_priv_key(struct l_settings *settings,
+ const char *value, const char *passphrase,
+ bool *is_encrypted)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_private_key(value, passphrase, is_encrypted);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_private_key_from_data(pem, strlen(pem),
+ passphrase, is_encrypted);
+}
+
int eap_tls_common_settings_check(struct l_settings *settings,
struct l_queue *secrets,
const char *prefix,
@@ -813,16 +872,17 @@ int eap_tls_common_settings_check(struct l_settings *settings,
struct l_key *pub_key;
const char *domain_mask_str;
- L_AUTO_FREE_VAR(char *, path);
+ L_AUTO_FREE_VAR(char *, value);
L_AUTO_FREE_VAR(char *, client_cert) = NULL;
L_AUTO_FREE_VAR(char *, passphrase) = NULL;
snprintf(setting_key, sizeof(setting_key), "%sCACert", prefix);
- path = l_settings_get_string(settings, "Security", setting_key);
- if (path) {
- cacerts = l_pem_load_certificate_list(path);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ cacerts = eap_tls_load_ca_cert(settings, value);
+
if (!cacerts) {
- l_error("Failed to load %s", path);
+ l_error("Failed to load %s", value);
return -EIO;
}
}
@@ -832,7 +892,8 @@ int eap_tls_common_settings_check(struct l_settings *settings,
client_cert = l_settings_get_string(settings, "Security",
client_cert_setting);
if (client_cert) {
- cert = l_pem_load_certificate_chain(client_cert);
+ cert = eap_tls_load_client_cert(settings, client_cert);
+
if (!cert) {
l_error("Failed to load %s", client_cert);
ret = -EIO;
@@ -843,7 +904,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (cacerts)
l_error("Certificate chain %s is not trusted "
"by any CA in %s or fails verification"
- ": %s", client_cert, path, error_str);
+ ": %s", client_cert, value, error_str);
else
l_error("Certificate chain %s fails "
"verification: %s",
@@ -854,17 +915,17 @@ int eap_tls_common_settings_check(struct l_settings *settings,
}
}
- l_free(path);
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
- path = l_settings_get_string(settings, "Security", setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
- if (path && !client_cert) {
+ if (value && !client_cert) {
l_error("%s present but no client certificate (%s)",
setting_key, client_cert_setting);
ret = -ENOENT;
goto done;
- } else if (!path && client_cert) {
+ } else if (!value && client_cert) {
l_error("%s present but no client private key (%s)",
client_cert_setting, setting_key);
ret = -ENOENT;
@@ -885,10 +946,11 @@ int eap_tls_common_settings_check(struct l_settings *settings,
passphrase = l_strdup(secret->value);
}
- if (!path) {
+ if (!value) {
if (passphrase) {
- l_error("%s present but no client private key path set (%s)",
- passphrase_setting, setting_key);
+ l_error("%s present but no client private key"
+ " value set (%s)", passphrase_setting,
+ setting_key);
ret = -ENOENT;
goto done;
}
@@ -897,17 +959,19 @@ int eap_tls_common_settings_check(struct l_settings *settings,
goto done;
}
- priv_key = l_pem_load_private_key(path, passphrase, &is_encrypted);
+ priv_key = eap_tls_load_priv_key(settings, value, passphrase,
+ &is_encrypted);
+
if (!priv_key) {
if (!is_encrypted) {
- l_error("Error loading client private key %s", path);
+ l_error("Error loading client private key %s", value);
ret = -EIO;
goto done;
}
if (passphrase) {
l_error("Error loading encrypted client private key %s",
- path);
+ value);
ret = -EACCES;
goto done;
}
@@ -918,7 +982,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
*/
eap_append_secret(out_missing,
EAP_SECRET_LOCAL_PKEY_PASSPHRASE,
- passphrase_setting, NULL, path,
+ passphrase_setting, NULL, value,
EAP_CACHE_TEMPORARY);
ret = 0;
goto done;
@@ -926,7 +990,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (passphrase && !is_encrypted) {
l_error("%s present but client private key %s is not encrypted",
- passphrase_setting, path);
+ passphrase_setting, value);
ret = -ENOENT;
goto done;
}
@@ -934,7 +998,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (!l_key_get_info(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
&size, &is_public) || is_public) {
l_error("%s is not a private key or l_key_get_info fails",
- path);
+ value);
ret = -EINVAL;
goto done;
}
@@ -964,14 +1028,14 @@ int eap_tls_common_settings_check(struct l_settings *settings,
result = l_key_decrypt(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
encrypted, decrypted, size, size);
if (result < 0) {
- l_error("l_key_decrypt fails with %s: %s", path,
+ l_error("l_key_decrypt fails with %s: %s", value,
strerror(-result));
ret = result;
goto done;
}
if (result != 1 || decrypted[0] != 0) {
- l_error("Private key %s does not match certificate %s", path,
+ l_error("Private key %s does not match certificate %s", value,
client_cert);
ret = -EINVAL;
goto done;
@@ -1014,6 +1078,8 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
struct eap_tls_state *eap_tls;
char setting_key[72];
char *domain_mask_str;
+ L_AUTO_FREE_VAR(char *, value) = NULL;
+ L_AUTO_FREE_VAR(char *, passphrase) = NULL;
eap_tls = l_new(struct eap_tls_state, 1);
@@ -1022,21 +1088,45 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
eap_tls->variant_data = variant_data;
snprintf(setting_key, sizeof(setting_key), "%sCACert", prefix);
- eap_tls->ca_cert = l_settings_get_string(settings, "Security",
- setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->ca_cert = eap_tls_load_ca_cert(settings, value);
+ if (!eap_tls->ca_cert) {
+ l_error("Could not load CACert %s", value);
+ goto load_error;
+ }
+ }
+
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientCert", prefix);
- eap_tls->client_cert = l_settings_get_string(settings, "Security",
- setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->client_cert = eap_tls_load_client_cert(settings,
+ value);
+ if (!eap_tls->ca_cert) {
+ l_error("Could not load ClientCert %s", value);
+ goto load_error;
+ }
+ }
- snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
- eap_tls->client_key = l_settings_get_string(settings, "Security",
- setting_key);
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientKeyPassphrase",
prefix);
- eap_tls->passphrase = l_settings_get_string(settings, "Security",
- setting_key);
+ passphrase = l_settings_get_string(settings, "Security", setting_key);
+
+ snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->client_key = eap_tls_load_priv_key(settings, value,
+ passphrase,
+ NULL);
+ if (!eap_tls->client_key) {
+ l_error("Could not load ClientKey %s", value);
+ goto load_error;
+ }
+ }
snprintf(setting_key, sizeof(setting_key), "%sServerDomainMask",
prefix);
@@ -1051,6 +1141,11 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
eap_set_data(eap, eap_tls);
return true;
+
+load_error:
+ __eap_tls_common_state_free(eap_tls);
+
+ return false;
}
void eap_tls_common_set_completed(struct eap_state *eap)
--
2.17.1
1 year, 4 months
[PATCH v2 1/3] eap-tls-common: allow embedded PEMs in settings
by James Prestwood
Refactoring was required to allow for embedded certs. The existing
eap_tls_state object was changed to hold the cert types (l_queue,
l_certchain, l_key) rather than the file path, since there may not
actually be separate PEM files.
Care was taken to properly manage the memory of these objects.
Since the TLS object takes ownership when setting auth data or the
CA certs all error cases must be handled properly to free these
objects after they are loaded and in addition they must be set to
NULL so that the cleanup doesn't double free them.
If everything goes to plan, we load all the PEMs in settings_load,
provide these objects to the TLS APIs, and then NULL out the
pointers (TLS now owns this memory). If anything fails between
settings_load and l_tls_start we must free these objects.
A special format must be used to indicate that a PEM is embedded
inside the settings file. First, the l_settings format should be
followed for the PEM itself, e.g.
[@pem@my_ca_cert]
<CA Cert data>
This PEM can then be referenced by "embed:my_ca_cert", e.g.
EAP-TLS-CACert=embed:my_ca_cert
Any other value not starting with "embed:" will be treated as a file
path.
---
src/eap-tls-common.c | 259 +++++++++++++++++++++++++++++--------------
1 file changed, 177 insertions(+), 82 deletions(-)
diff --git a/src/eap-tls-common.c b/src/eap-tls-common.c
index 39015167..b76770ce 100644
--- a/src/eap-tls-common.c
+++ b/src/eap-tls-common.c
@@ -113,10 +113,9 @@ struct eap_tls_state {
bool expecting_frag_ack:1;
- char *ca_cert;
- char *client_cert;
- char *client_key;
- char *passphrase;
+ struct l_queue *ca_cert;
+ struct l_certchain *client_cert;
+ struct l_key *client_key;
char **domain_mask;
const struct eap_tls_variant_ops *variant_ops;
@@ -154,6 +153,21 @@ static void __eap_tls_common_state_reset(struct eap_tls_state *eap_tls)
}
}
+static void __eap_tls_common_state_free(struct eap_tls_state *eap_tls)
+{
+ if (eap_tls->ca_cert)
+ l_queue_destroy(eap_tls->ca_cert,
+ (l_queue_destroy_func_t)l_cert_free);
+ if (eap_tls->client_cert)
+ l_certchain_free(eap_tls->client_cert);
+
+ if (eap_tls->client_key)
+ l_key_free(eap_tls->client_key);
+
+ l_strv_free(eap_tls->domain_mask);
+ l_free(eap_tls);
+}
+
bool eap_tls_common_state_reset(struct eap_state *eap)
{
struct eap_tls_state *eap_tls = eap_get_data(eap);
@@ -177,18 +191,7 @@ void eap_tls_common_state_free(struct eap_state *eap)
if (eap_tls->variant_ops->destroy)
eap_tls->variant_ops->destroy(eap_tls->variant_data);
- l_free(eap_tls->ca_cert);
- l_free(eap_tls->client_cert);
- l_free(eap_tls->client_key);
-
- if (eap_tls->passphrase) {
- explicit_bzero(eap_tls->passphrase,
- strlen(eap_tls->passphrase));
- l_free(eap_tls->passphrase);
- }
-
- l_strv_free(eap_tls->domain_mask);
- l_free(eap_tls);
+ __eap_tls_common_state_free(eap_tls);
}
static void eap_tls_tunnel_debug(const char *str, void *user_data)
@@ -544,55 +547,34 @@ static bool eap_tls_tunnel_init(struct eap_state *eap)
NULL);
if (eap_tls->client_cert || eap_tls->client_key) {
- struct l_certchain *client_cert =
- l_pem_load_certificate_chain(eap_tls->client_cert);
- struct l_key *client_key;
-
- if (!client_cert) {
- l_error("%s: Failed to parse client certificate: %s.",
- eap_get_method_name(eap),
- eap_tls->client_cert);
- return false;
- }
+ if (!l_tls_set_auth_data(eap_tls->tunnel, eap_tls->client_cert,
+ eap_tls->client_key)) {
+ l_certchain_free(eap_tls->client_cert);
+ eap_tls->client_cert = NULL;
- client_key = l_pem_load_private_key(eap_tls->client_key,
- eap_tls->passphrase,
- NULL);
- if (!client_key) {
- l_error("%s: Failed to parse client private key: %s.",
- eap_get_method_name(eap),
- eap_tls->client_key);
- return false;
- }
+ l_key_free(eap_tls->client_key);
+ eap_tls->client_key = NULL;
- if (!l_tls_set_auth_data(eap_tls->tunnel,
- client_cert, client_key)) {
- l_certchain_free(client_cert);
- l_key_free(client_key);
l_error("%s: Failed to set auth data.",
eap_get_method_name(eap));
return false;
}
+
+ eap_tls->client_cert = NULL;
+ eap_tls->client_key = NULL;
}
if (eap_tls->ca_cert) {
- struct l_queue *ca_cert =
- l_pem_load_certificate_list(eap_tls->ca_cert);
-
- if (!ca_cert) {
- l_error("%s: Error loading CA certificates from %s.",
- eap_get_method_name(eap),
- eap_tls->ca_cert);
- return false;
- }
-
- if (!l_tls_set_cacert(eap_tls->tunnel, ca_cert)) {
- l_queue_destroy(ca_cert,
+ if (!l_tls_set_cacert(eap_tls->tunnel, eap_tls->ca_cert)) {
+ l_queue_destroy(eap_tls->ca_cert,
(l_queue_destroy_func_t)l_cert_free);
+ eap_tls->ca_cert = NULL;
l_error("%s: Error settings CA certificates.",
eap_get_method_name(eap));
return false;
}
+
+ eap_tls->ca_cert = NULL;
}
if (eap_tls->domain_mask)
@@ -793,6 +775,83 @@ error:
eap_method_error(eap);
}
+static const char *load_embedded_pem(struct l_settings *settings,
+ const char *name)
+{
+ const char *pem;
+ const char *type;
+
+ pem = l_settings_get_embedded_value(settings, name + 6, &type);
+ if (!pem)
+ return NULL;
+
+ if (strcmp(type, "pem"))
+ return NULL;
+
+ return pem;
+}
+
+static bool is_embedded(const char *str)
+{
+ if (!str)
+ return false;
+
+ if (strlen(str) < 6)
+ return false;
+
+ if (!strncmp("embed:", str, 6))
+ return true;
+
+ return false;
+}
+
+static struct l_queue *eap_tls_load_ca_cert(struct l_settings *settings,
+ const char *value)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_certificate_list(value);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_certificate_list_from_data(pem, strlen(pem));
+}
+
+static struct l_certchain *eap_tls_load_client_cert(struct l_settings *settings,
+ const char *value)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_certificate_chain(value);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_certificate_chain_from_data(pem, strlen(pem));
+}
+
+static struct l_key *eap_tls_load_priv_key(struct l_settings *settings,
+ const char *value, const char *passphrase,
+ bool *is_encrypted)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_private_key(value, passphrase, is_encrypted);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_private_key_from_data(pem, strlen(pem),
+ passphrase, is_encrypted);
+}
+
int eap_tls_common_settings_check(struct l_settings *settings,
struct l_queue *secrets,
const char *prefix,
@@ -813,16 +872,17 @@ int eap_tls_common_settings_check(struct l_settings *settings,
struct l_key *pub_key;
const char *domain_mask_str;
- L_AUTO_FREE_VAR(char *, path);
+ L_AUTO_FREE_VAR(char *, value);
L_AUTO_FREE_VAR(char *, client_cert) = NULL;
L_AUTO_FREE_VAR(char *, passphrase) = NULL;
snprintf(setting_key, sizeof(setting_key), "%sCACert", prefix);
- path = l_settings_get_string(settings, "Security", setting_key);
- if (path) {
- cacerts = l_pem_load_certificate_list(path);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ cacerts = eap_tls_load_ca_cert(settings, value);
+
if (!cacerts) {
- l_error("Failed to load %s", path);
+ l_error("Failed to load %s", value);
return -EIO;
}
}
@@ -832,7 +892,8 @@ int eap_tls_common_settings_check(struct l_settings *settings,
client_cert = l_settings_get_string(settings, "Security",
client_cert_setting);
if (client_cert) {
- cert = l_pem_load_certificate_chain(client_cert);
+ cert = eap_tls_load_client_cert(settings, client_cert);
+
if (!cert) {
l_error("Failed to load %s", client_cert);
ret = -EIO;
@@ -843,7 +904,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (cacerts)
l_error("Certificate chain %s is not trusted "
"by any CA in %s or fails verification"
- ": %s", client_cert, path, error_str);
+ ": %s", client_cert, value, error_str);
else
l_error("Certificate chain %s fails "
"verification: %s",
@@ -854,17 +915,17 @@ int eap_tls_common_settings_check(struct l_settings *settings,
}
}
- l_free(path);
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
- path = l_settings_get_string(settings, "Security", setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
- if (path && !client_cert) {
+ if (value && !client_cert) {
l_error("%s present but no client certificate (%s)",
setting_key, client_cert_setting);
ret = -ENOENT;
goto done;
- } else if (!path && client_cert) {
+ } else if (!value && client_cert) {
l_error("%s present but no client private key (%s)",
client_cert_setting, setting_key);
ret = -ENOENT;
@@ -885,10 +946,11 @@ int eap_tls_common_settings_check(struct l_settings *settings,
passphrase = l_strdup(secret->value);
}
- if (!path) {
+ if (!value) {
if (passphrase) {
- l_error("%s present but no client private key path set (%s)",
- passphrase_setting, setting_key);
+ l_error("%s present but no client private key"
+ " value set (%s)", passphrase_setting,
+ setting_key);
ret = -ENOENT;
goto done;
}
@@ -897,17 +959,19 @@ int eap_tls_common_settings_check(struct l_settings *settings,
goto done;
}
- priv_key = l_pem_load_private_key(path, passphrase, &is_encrypted);
+ priv_key = eap_tls_load_priv_key(settings, value, passphrase,
+ &is_encrypted);
+
if (!priv_key) {
if (!is_encrypted) {
- l_error("Error loading client private key %s", path);
+ l_error("Error loading client private key %s", value);
ret = -EIO;
goto done;
}
if (passphrase) {
l_error("Error loading encrypted client private key %s",
- path);
+ value);
ret = -EACCES;
goto done;
}
@@ -918,7 +982,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
*/
eap_append_secret(out_missing,
EAP_SECRET_LOCAL_PKEY_PASSPHRASE,
- passphrase_setting, NULL, path,
+ passphrase_setting, NULL, value,
EAP_CACHE_TEMPORARY);
ret = 0;
goto done;
@@ -926,7 +990,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (passphrase && !is_encrypted) {
l_error("%s present but client private key %s is not encrypted",
- passphrase_setting, path);
+ passphrase_setting, value);
ret = -ENOENT;
goto done;
}
@@ -934,7 +998,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (!l_key_get_info(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
&size, &is_public) || is_public) {
l_error("%s is not a private key or l_key_get_info fails",
- path);
+ value);
ret = -EINVAL;
goto done;
}
@@ -964,14 +1028,14 @@ int eap_tls_common_settings_check(struct l_settings *settings,
result = l_key_decrypt(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
encrypted, decrypted, size, size);
if (result < 0) {
- l_error("l_key_decrypt fails with %s: %s", path,
+ l_error("l_key_decrypt fails with %s: %s", value,
strerror(-result));
ret = result;
goto done;
}
if (result != 1 || decrypted[0] != 0) {
- l_error("Private key %s does not match certificate %s", path,
+ l_error("Private key %s does not match certificate %s", value,
client_cert);
ret = -EINVAL;
goto done;
@@ -1014,6 +1078,8 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
struct eap_tls_state *eap_tls;
char setting_key[72];
char *domain_mask_str;
+ L_AUTO_FREE_VAR(char *, value) = NULL;
+ L_AUTO_FREE_VAR(char *, passphrase) = NULL;
eap_tls = l_new(struct eap_tls_state, 1);
@@ -1022,21 +1088,45 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
eap_tls->variant_data = variant_data;
snprintf(setting_key, sizeof(setting_key), "%sCACert", prefix);
- eap_tls->ca_cert = l_settings_get_string(settings, "Security",
- setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->ca_cert = eap_tls_load_ca_cert(settings, value);
+ if (!eap_tls->ca_cert) {
+ l_error("Could not load CACert %s", value);
+ goto load_error;
+ }
+ }
+
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientCert", prefix);
- eap_tls->client_cert = l_settings_get_string(settings, "Security",
- setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->client_cert = eap_tls_load_client_cert(settings,
+ value);
+ if (!eap_tls->ca_cert) {
+ l_error("Could not load ClientCert %s", value);
+ goto load_error;
+ }
+ }
- snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
- eap_tls->client_key = l_settings_get_string(settings, "Security",
- setting_key);
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientKeyPassphrase",
prefix);
- eap_tls->passphrase = l_settings_get_string(settings, "Security",
- setting_key);
+ passphrase = l_settings_get_string(settings, "Security", setting_key);
+
+ snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->client_key = eap_tls_load_priv_key(settings, value,
+ passphrase,
+ NULL);
+ if (!eap_tls->client_key) {
+ l_error("Could not load ClientKey %s", value);
+ goto load_error;
+ }
+ }
snprintf(setting_key, sizeof(setting_key), "%sServerDomainMask",
prefix);
@@ -1051,6 +1141,11 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
eap_set_data(eap, eap_tls);
return true;
+
+load_error:
+ __eap_tls_common_state_free(eap_tls);
+
+ return false;
}
void eap_tls_common_set_completed(struct eap_state *eap)
--
2.17.1
1 year, 4 months
[PATCH 1/3] eap-tls-common: allow embedded PEMs in settings
by James Prestwood
Refactoring was required to allow for embedded certs. The existing
eap_tls_state object was changed to hold the cert types (l_queue,
l_certchain, l_key) rather than the file path, since there may not
actually be separate PEM files.
Care was taken to properly manage the memory of these objects.
Since the TLS object takes ownership when setting auth data or the
CA certs all error cases must be handled properly to free these
objects after they are loaded and in addition they must be set to
NULL so that the cleanup doesn't double free them.
If everything goes to plan, we load all the PEMs in settings_load,
provide these objects to the TLS APIs, and then NULL out the
pointers (TLS now owns this memory). If anything fails between
settings_load and l_tls_start we must free these objects.
A special format must be used to indicate that a PEM is embedded
inside the settings file. First, the l_settings format should be
followed for the PEM itself, e.g.
[@pem@my_ca_cert]
<CA Cert data>
This PEM can then be referenced by "embed:my_ca_cert", e.g.
EAP-TLS-CACert=embed:my_ca_cert
Any other value not starting with "embed:" will be treated as a file
path.
---
src/eap-tls-common.c | 259 +++++++++++++++++++++++++++++--------------
1 file changed, 177 insertions(+), 82 deletions(-)
diff --git a/src/eap-tls-common.c b/src/eap-tls-common.c
index 39015167..b76770ce 100644
--- a/src/eap-tls-common.c
+++ b/src/eap-tls-common.c
@@ -113,10 +113,9 @@ struct eap_tls_state {
bool expecting_frag_ack:1;
- char *ca_cert;
- char *client_cert;
- char *client_key;
- char *passphrase;
+ struct l_queue *ca_cert;
+ struct l_certchain *client_cert;
+ struct l_key *client_key;
char **domain_mask;
const struct eap_tls_variant_ops *variant_ops;
@@ -154,6 +153,21 @@ static void __eap_tls_common_state_reset(struct eap_tls_state *eap_tls)
}
}
+static void __eap_tls_common_state_free(struct eap_tls_state *eap_tls)
+{
+ if (eap_tls->ca_cert)
+ l_queue_destroy(eap_tls->ca_cert,
+ (l_queue_destroy_func_t)l_cert_free);
+ if (eap_tls->client_cert)
+ l_certchain_free(eap_tls->client_cert);
+
+ if (eap_tls->client_key)
+ l_key_free(eap_tls->client_key);
+
+ l_strv_free(eap_tls->domain_mask);
+ l_free(eap_tls);
+}
+
bool eap_tls_common_state_reset(struct eap_state *eap)
{
struct eap_tls_state *eap_tls = eap_get_data(eap);
@@ -177,18 +191,7 @@ void eap_tls_common_state_free(struct eap_state *eap)
if (eap_tls->variant_ops->destroy)
eap_tls->variant_ops->destroy(eap_tls->variant_data);
- l_free(eap_tls->ca_cert);
- l_free(eap_tls->client_cert);
- l_free(eap_tls->client_key);
-
- if (eap_tls->passphrase) {
- explicit_bzero(eap_tls->passphrase,
- strlen(eap_tls->passphrase));
- l_free(eap_tls->passphrase);
- }
-
- l_strv_free(eap_tls->domain_mask);
- l_free(eap_tls);
+ __eap_tls_common_state_free(eap_tls);
}
static void eap_tls_tunnel_debug(const char *str, void *user_data)
@@ -544,55 +547,34 @@ static bool eap_tls_tunnel_init(struct eap_state *eap)
NULL);
if (eap_tls->client_cert || eap_tls->client_key) {
- struct l_certchain *client_cert =
- l_pem_load_certificate_chain(eap_tls->client_cert);
- struct l_key *client_key;
-
- if (!client_cert) {
- l_error("%s: Failed to parse client certificate: %s.",
- eap_get_method_name(eap),
- eap_tls->client_cert);
- return false;
- }
+ if (!l_tls_set_auth_data(eap_tls->tunnel, eap_tls->client_cert,
+ eap_tls->client_key)) {
+ l_certchain_free(eap_tls->client_cert);
+ eap_tls->client_cert = NULL;
- client_key = l_pem_load_private_key(eap_tls->client_key,
- eap_tls->passphrase,
- NULL);
- if (!client_key) {
- l_error("%s: Failed to parse client private key: %s.",
- eap_get_method_name(eap),
- eap_tls->client_key);
- return false;
- }
+ l_key_free(eap_tls->client_key);
+ eap_tls->client_key = NULL;
- if (!l_tls_set_auth_data(eap_tls->tunnel,
- client_cert, client_key)) {
- l_certchain_free(client_cert);
- l_key_free(client_key);
l_error("%s: Failed to set auth data.",
eap_get_method_name(eap));
return false;
}
+
+ eap_tls->client_cert = NULL;
+ eap_tls->client_key = NULL;
}
if (eap_tls->ca_cert) {
- struct l_queue *ca_cert =
- l_pem_load_certificate_list(eap_tls->ca_cert);
-
- if (!ca_cert) {
- l_error("%s: Error loading CA certificates from %s.",
- eap_get_method_name(eap),
- eap_tls->ca_cert);
- return false;
- }
-
- if (!l_tls_set_cacert(eap_tls->tunnel, ca_cert)) {
- l_queue_destroy(ca_cert,
+ if (!l_tls_set_cacert(eap_tls->tunnel, eap_tls->ca_cert)) {
+ l_queue_destroy(eap_tls->ca_cert,
(l_queue_destroy_func_t)l_cert_free);
+ eap_tls->ca_cert = NULL;
l_error("%s: Error settings CA certificates.",
eap_get_method_name(eap));
return false;
}
+
+ eap_tls->ca_cert = NULL;
}
if (eap_tls->domain_mask)
@@ -793,6 +775,83 @@ error:
eap_method_error(eap);
}
+static const char *load_embedded_pem(struct l_settings *settings,
+ const char *name)
+{
+ const char *pem;
+ const char *type;
+
+ pem = l_settings_get_embedded_value(settings, name + 6, &type);
+ if (!pem)
+ return NULL;
+
+ if (strcmp(type, "pem"))
+ return NULL;
+
+ return pem;
+}
+
+static bool is_embedded(const char *str)
+{
+ if (!str)
+ return false;
+
+ if (strlen(str) < 6)
+ return false;
+
+ if (!strncmp("embed:", str, 6))
+ return true;
+
+ return false;
+}
+
+static struct l_queue *eap_tls_load_ca_cert(struct l_settings *settings,
+ const char *value)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_certificate_list(value);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_certificate_list_from_data(pem, strlen(pem));
+}
+
+static struct l_certchain *eap_tls_load_client_cert(struct l_settings *settings,
+ const char *value)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_certificate_chain(value);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_certificate_chain_from_data(pem, strlen(pem));
+}
+
+static struct l_key *eap_tls_load_priv_key(struct l_settings *settings,
+ const char *value, const char *passphrase,
+ bool *is_encrypted)
+{
+ const char *pem;
+
+ if (!is_embedded(value))
+ return l_pem_load_private_key(value, passphrase, is_encrypted);
+
+ pem = load_embedded_pem(settings, value);
+ if (!pem)
+ return NULL;
+
+ return l_pem_load_private_key_from_data(pem, strlen(pem),
+ passphrase, is_encrypted);
+}
+
int eap_tls_common_settings_check(struct l_settings *settings,
struct l_queue *secrets,
const char *prefix,
@@ -813,16 +872,17 @@ int eap_tls_common_settings_check(struct l_settings *settings,
struct l_key *pub_key;
const char *domain_mask_str;
- L_AUTO_FREE_VAR(char *, path);
+ L_AUTO_FREE_VAR(char *, value);
L_AUTO_FREE_VAR(char *, client_cert) = NULL;
L_AUTO_FREE_VAR(char *, passphrase) = NULL;
snprintf(setting_key, sizeof(setting_key), "%sCACert", prefix);
- path = l_settings_get_string(settings, "Security", setting_key);
- if (path) {
- cacerts = l_pem_load_certificate_list(path);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ cacerts = eap_tls_load_ca_cert(settings, value);
+
if (!cacerts) {
- l_error("Failed to load %s", path);
+ l_error("Failed to load %s", value);
return -EIO;
}
}
@@ -832,7 +892,8 @@ int eap_tls_common_settings_check(struct l_settings *settings,
client_cert = l_settings_get_string(settings, "Security",
client_cert_setting);
if (client_cert) {
- cert = l_pem_load_certificate_chain(client_cert);
+ cert = eap_tls_load_client_cert(settings, client_cert);
+
if (!cert) {
l_error("Failed to load %s", client_cert);
ret = -EIO;
@@ -843,7 +904,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (cacerts)
l_error("Certificate chain %s is not trusted "
"by any CA in %s or fails verification"
- ": %s", client_cert, path, error_str);
+ ": %s", client_cert, value, error_str);
else
l_error("Certificate chain %s fails "
"verification: %s",
@@ -854,17 +915,17 @@ int eap_tls_common_settings_check(struct l_settings *settings,
}
}
- l_free(path);
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
- path = l_settings_get_string(settings, "Security", setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
- if (path && !client_cert) {
+ if (value && !client_cert) {
l_error("%s present but no client certificate (%s)",
setting_key, client_cert_setting);
ret = -ENOENT;
goto done;
- } else if (!path && client_cert) {
+ } else if (!value && client_cert) {
l_error("%s present but no client private key (%s)",
client_cert_setting, setting_key);
ret = -ENOENT;
@@ -885,10 +946,11 @@ int eap_tls_common_settings_check(struct l_settings *settings,
passphrase = l_strdup(secret->value);
}
- if (!path) {
+ if (!value) {
if (passphrase) {
- l_error("%s present but no client private key path set (%s)",
- passphrase_setting, setting_key);
+ l_error("%s present but no client private key"
+ " value set (%s)", passphrase_setting,
+ setting_key);
ret = -ENOENT;
goto done;
}
@@ -897,17 +959,19 @@ int eap_tls_common_settings_check(struct l_settings *settings,
goto done;
}
- priv_key = l_pem_load_private_key(path, passphrase, &is_encrypted);
+ priv_key = eap_tls_load_priv_key(settings, value, passphrase,
+ &is_encrypted);
+
if (!priv_key) {
if (!is_encrypted) {
- l_error("Error loading client private key %s", path);
+ l_error("Error loading client private key %s", value);
ret = -EIO;
goto done;
}
if (passphrase) {
l_error("Error loading encrypted client private key %s",
- path);
+ value);
ret = -EACCES;
goto done;
}
@@ -918,7 +982,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
*/
eap_append_secret(out_missing,
EAP_SECRET_LOCAL_PKEY_PASSPHRASE,
- passphrase_setting, NULL, path,
+ passphrase_setting, NULL, value,
EAP_CACHE_TEMPORARY);
ret = 0;
goto done;
@@ -926,7 +990,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (passphrase && !is_encrypted) {
l_error("%s present but client private key %s is not encrypted",
- passphrase_setting, path);
+ passphrase_setting, value);
ret = -ENOENT;
goto done;
}
@@ -934,7 +998,7 @@ int eap_tls_common_settings_check(struct l_settings *settings,
if (!l_key_get_info(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
&size, &is_public) || is_public) {
l_error("%s is not a private key or l_key_get_info fails",
- path);
+ value);
ret = -EINVAL;
goto done;
}
@@ -964,14 +1028,14 @@ int eap_tls_common_settings_check(struct l_settings *settings,
result = l_key_decrypt(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
encrypted, decrypted, size, size);
if (result < 0) {
- l_error("l_key_decrypt fails with %s: %s", path,
+ l_error("l_key_decrypt fails with %s: %s", value,
strerror(-result));
ret = result;
goto done;
}
if (result != 1 || decrypted[0] != 0) {
- l_error("Private key %s does not match certificate %s", path,
+ l_error("Private key %s does not match certificate %s", value,
client_cert);
ret = -EINVAL;
goto done;
@@ -1014,6 +1078,8 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
struct eap_tls_state *eap_tls;
char setting_key[72];
char *domain_mask_str;
+ L_AUTO_FREE_VAR(char *, value) = NULL;
+ L_AUTO_FREE_VAR(char *, passphrase) = NULL;
eap_tls = l_new(struct eap_tls_state, 1);
@@ -1022,21 +1088,45 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
eap_tls->variant_data = variant_data;
snprintf(setting_key, sizeof(setting_key), "%sCACert", prefix);
- eap_tls->ca_cert = l_settings_get_string(settings, "Security",
- setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->ca_cert = eap_tls_load_ca_cert(settings, value);
+ if (!eap_tls->ca_cert) {
+ l_error("Could not load CACert %s", value);
+ goto load_error;
+ }
+ }
+
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientCert", prefix);
- eap_tls->client_cert = l_settings_get_string(settings, "Security",
- setting_key);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->client_cert = eap_tls_load_client_cert(settings,
+ value);
+ if (!eap_tls->ca_cert) {
+ l_error("Could not load ClientCert %s", value);
+ goto load_error;
+ }
+ }
- snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
- eap_tls->client_key = l_settings_get_string(settings, "Security",
- setting_key);
+ l_free(value);
snprintf(setting_key, sizeof(setting_key), "%sClientKeyPassphrase",
prefix);
- eap_tls->passphrase = l_settings_get_string(settings, "Security",
- setting_key);
+ passphrase = l_settings_get_string(settings, "Security", setting_key);
+
+ snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
+ value = l_settings_get_string(settings, "Security", setting_key);
+ if (value) {
+ eap_tls->client_key = eap_tls_load_priv_key(settings, value,
+ passphrase,
+ NULL);
+ if (!eap_tls->client_key) {
+ l_error("Could not load ClientKey %s", value);
+ goto load_error;
+ }
+ }
snprintf(setting_key, sizeof(setting_key), "%sServerDomainMask",
prefix);
@@ -1051,6 +1141,11 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
eap_set_data(eap, eap_tls);
return true;
+
+load_error:
+ __eap_tls_common_state_free(eap_tls);
+
+ return false;
}
void eap_tls_common_set_completed(struct eap_state *eap)
--
2.17.1
1 year, 4 months