diff --git a/etc/Makefile.am b/etc/Makefile.am index a5c9b880..64dba474 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -6,6 +6,7 @@ EXTRA_DIST = mptcpd.conf.in pkgsysconf_DATA = mptcpd.conf pkgsysconfdir = $(sysconfdir)/@PACKAGE@ +pluginsconfdir = $(pkgsysconfdir)/plugins.conf.d ## The configure script won't fully expand $pkglibdir so leverage ## `make' based variable expansion instead. @@ -27,8 +28,11 @@ CLEANFILES = mptcpd.conf # writable. install-data-hook: installcheck-local chmod o-w $(DESTDIR)$(pkgsysconfdir) + $(MKDIR_P) $(DESTDIR)$(pluginsconfdir) + chmod o-w $(DESTDIR)$(pluginsconfdir) installcheck-local: - $(top_srcdir)/scripts/check-permissions \ - $(DESTDIR)$(pkgsysconfdir) \ - $(DESTDIR)$(pkgsysconfdir)/mptcpd.conf + $(top_srcdir)/scripts/check-permissions \ + $(DESTDIR)$(pkgsysconfdir) \ + $(DESTDIR)$(pkgsysconfdir)/mptcpd.conf \ + $(DESTDIR)$(pluginsconfdir) diff --git a/include/mptcpd/plugin.h b/include/mptcpd/plugin.h index 58683d08..8b2571fe 100644 --- a/include/mptcpd/plugin.h +++ b/include/mptcpd/plugin.h @@ -353,6 +353,10 @@ MPTCPD_API bool mptcpd_plugin_register_ops( char const *name, struct mptcpd_plugin_ops const *ops); +MPTCPD_API bool mptcpd_plugin_read_config(char const *filename, + mptcpd_parse_func_t fun, + void *user_data); + #ifdef __cplusplus } #endif diff --git a/include/mptcpd/private/configuration.h b/include/mptcpd/private/configuration.h index 0625cd20..b6d1e0e9 100644 --- a/include/mptcpd/private/configuration.h +++ b/include/mptcpd/private/configuration.h @@ -10,6 +10,8 @@ #ifndef MPTCPD_CONFIGURATION_H #define MPTCPD_CONFIGURATION_H +#include +#include /** * Function pointer corresponding to the ELL functions that set the @@ -66,6 +68,9 @@ struct mptcpd_config /// A list of plugins to load. struct l_queue *plugins_to_load; + + /// Location of mptcpd plugins configuration files + char *plugins_conf_dir; }; /** @@ -87,6 +92,10 @@ struct mptcpd_config *mptcpd_config_create(int argc, char *argv[]); */ void mptcpd_config_destroy(struct mptcpd_config *config); +MPTCPD_API bool mptcpd_config_read(char const *filename, + mptcpd_parse_func_t fun, + void *user_data); + #endif // MPTCPD_CONFIGURATION_H /* diff --git a/include/mptcpd/private/plugin.h b/include/mptcpd/private/plugin.h index ad25ca85..fe80eb1b 100644 --- a/include/mptcpd/private/plugin.h +++ b/include/mptcpd/private/plugin.h @@ -42,6 +42,7 @@ struct mptcpd_interface; */ MPTCPD_API bool mptcpd_plugin_load(char const *dir, char const *default_name, + char const *plugins_conf_dir, struct l_queue const *plugins_to_load, struct mptcpd_pm *pm); diff --git a/include/mptcpd/types.h b/include/mptcpd/types.h index ed16a810..4b7b0582 100644 --- a/include/mptcpd/types.h +++ b/include/mptcpd/types.h @@ -17,6 +17,8 @@ extern "C" { #endif +struct l_settings; + /** * @todo These rely on MPTCP genl related implementation details in * the kernel. Should we move these typedefs to @@ -165,6 +167,10 @@ typedef void (*mptcpd_pm_get_limits_cb)( size_t len, void *callback_data); +typedef void (*mptcpd_parse_func_t) ( + struct l_settings const* settings, + void *user_data); + #ifdef __cplusplus } #endif diff --git a/lib/Makefile.am b/lib/Makefile.am index 990efaca..8cc8e544 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -20,6 +20,7 @@ libmptcpd_la_LDFLAGS = \ libmptcpd_la_SOURCES = \ addr_info.c \ + configuration.c \ id_manager.c \ listener_manager.c \ network_monitor.c \ diff --git a/lib/configuration.c b/lib/configuration.c new file mode 100644 index 00000000..70dc4be8 --- /dev/null +++ b/lib/configuration.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +/** + * @brief Verify file permissions are secure. + * + * Mptcpd requires that its files are only writable by the owner and + * group. Verify that the "other" write mode, @c S_IWOTH, isn't set. + * + * @param[in] f Name of file to check for expected permissions. + * + * @note There is a TOCTOU race condition between this file + * permissions check and subsequent calls to functions that + * access the given file @a f, such as the call to + * @c l_settings_load_from_file(). There is currently no way + * to avoid that with the existing ELL API. + */ +static bool check_file_perms(char const *f) +{ + struct stat sb; + bool perms_ok = false; + + if (stat(f, &sb) == 0) { + perms_ok = S_ISREG(sb.st_mode) + && (sb.st_mode & S_IWOTH) == 0; + + if (!perms_ok) + l_error("\"%s\" should be a file that is not " + "world writable.", + f); + } else if (errno == ENOENT) { + perms_ok = true; + + l_debug("File \"%s\" does not exist.", f); + } else { + l_debug("Unexpected error during file " + "permissions check."); + } + + return perms_ok; +} + +bool mptcpd_config_read(char const *filename, + mptcpd_parse_func_t fun, + void *user_data) +{ + assert(filename != NULL); + assert(fun != NULL); + + if (!check_file_perms(filename)) + return false; + + struct l_settings *const settings = l_settings_new(); + if (settings == NULL) { + l_error("Unable to create mptcpd settings."); + + return false; + } + + if (l_settings_load_from_file(settings, filename)) + fun(settings, user_data); + else + l_debug("Unable to load mptcpd settings from file '%s'", + filename); + + l_settings_free(settings); + + return true; +} + diff --git a/lib/plugin.c b/lib/plugin.c index ecd44cfd..e4848b0f 100644 --- a/lib/plugin.c +++ b/lib/plugin.c @@ -42,6 +42,7 @@ # define MPTCP_PM_NAME_LEN GENL_NAMSIZ #endif +#include #include #include @@ -89,6 +90,8 @@ static char _default_name[MPTCP_PM_NAME_LEN + 1]; */ static struct mptcpd_plugin_ops const *_default_ops; +static char *_conf_dir; + // ---------------------------------------------------------------- // Implementation Details // ---------------------------------------------------------------- @@ -435,6 +438,7 @@ static void unload_plugins(struct mptcpd_pm *pm) bool mptcpd_plugin_load(char const *dir, char const *default_name, + char const *plugins_conf_dir, struct l_queue const *plugins_to_load, struct mptcpd_pm *pm) { @@ -443,6 +447,14 @@ bool mptcpd_plugin_load(char const *dir, return false; } + if (plugins_conf_dir == NULL) { + l_error("No plugins configuration directory specified."); + return false; + } + + if (_conf_dir == NULL) + _conf_dir = l_strdup(plugins_conf_dir); + if (_plugin_infos == NULL) _plugin_infos = l_queue_new(); @@ -572,6 +584,25 @@ bool mptcpd_plugin_register_ops(char const *name, return registered; } +bool mptcpd_plugin_read_config(char const *filename, + mptcpd_parse_func_t fun, + void *user_data) +{ + assert(filename != NULL); + assert(fun != NULL); + + char *const path = l_strdup_printf("%s/%s.conf", + _conf_dir, + filename); + + bool success = mptcpd_config_read(path, fun, user_data); + + l_free(path); + + return success; +} + + // ---------------------------------------------------------------- // Plugin Operation Callback Invocation // ---------------------------------------------------------------- diff --git a/src/Makefile.am b/src/Makefile.am index eeb2f027..51ba9942 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,10 +32,12 @@ libpath_manager_la_LIBADD = \ $(top_builddir)/lib/libmptcpd.la \ $(ELL_LIBS) $(CODE_COVERAGE_LIBS) -libpath_manager_la_CPPFLAGS = \ - $(AM_CPPFLAGS) \ +libpath_manager_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ -DMPTCPD_CONFIG_FILE='"$(sysconfdir)/@PACKAGE@/@PACKAGE@.conf"' \ - -DMPTCPD_DEFAULT_PLUGINDIR='"$(libdir)/@PACKAGE@"' + -DMPTCPD_DEFAULT_PLUGINDIR='"$(libdir)/@PACKAGE@"' \ + -DMPTCPD_DEFAULT_PLUGINSCONFDIR='"$(sysconfdir)/@PACKAGE@/$\ + plugins.conf.d/"' EXTRA_DIST = mptcp.service.in diff --git a/src/configuration.c b/src/configuration.c index baefbabb..f13b03c6 100644 --- a/src/configuration.c +++ b/src/configuration.c @@ -338,6 +338,23 @@ static void set_plugins_to_load(struct mptcpd_config *config, l_free((char *) plugins); } +/** + * @brief Set mptcpd plugins configuration directory. + * + * Set mptcpd plugins configuration directory in the mptcpd configuration, + * @a config, being careful to deallocate a previously set plugins + * configuration directory. + * + * @param[in,out] config Mptcpd configuration. + * @param[in] dir Mptcpd plugins configuration directory. Ownership + * of memory is transferred to @a config. + */ +static void set_plugins_conf_dir(struct mptcpd_config *config, + char *dir) +{ + reset_string(&config->plugins_conf_dir, dir); +} + // --------------------------------------------------------------- // Command line options // --------------------------------------------------------------- @@ -371,6 +388,9 @@ static char const doc[] = /// Command line option key for "--load-plugins" #define MPTCPD_LOAD_PLUGINS_KEY 0x104 + +/// Command line option key for "--plugins-conf-dir" +#define MPTCPD_PLUGINS_CONF_DIR_KEY 0x105 ///@} static struct argp_option const options[] = { @@ -414,6 +434,12 @@ static struct argp_option const options[] = { "Specify which plugins to load, e.g. --load-plugins=addr_adv," "sspi", 0 }, + { "plugins-conf-dir", + MPTCPD_PLUGINS_CONF_DIR_KEY, + "DIR", + 0, + "Set plugins configuration directory to DIR", + 0 }, { 0 } }; @@ -464,6 +490,14 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) set_plugins_to_load(config, l_strdup(arg)); break; + case MPTCPD_PLUGINS_CONF_DIR_KEY: + if (strlen(arg) == 0) + argp_error(state, + "Empty plugins configuration directory" + " command line option."); + + set_plugins_conf_dir(config, l_strdup(arg)); + break; default: return ARGP_ERR_UNKNOWN; }; @@ -499,45 +533,6 @@ parse_options(struct mptcpd_config *config, int argc, char *argv[]) // Configuration files // --------------------------------------------------------------- -/** - * @brief Verify file permissions are secure. - * - * Mptcpd requires that its files are only writable by the owner and - * group. Verify that the "other" write mode, @c S_IWOTH, isn't set. - * - * @param[in] f Name of file to check for expected permissions. - * - * @note There is a TOCTOU race condition between this file - * permissions check and subsequent calls to functions that - * access the given file @a f, such as the call to - * @c l_settings_load_from_file(). There is currently no way - * to avoid that with the existing ELL API. - */ -static bool check_file_perms(char const *f) -{ - struct stat sb; - bool perms_ok = false; - - if (stat(f, &sb) == 0) { - perms_ok = S_ISREG(sb.st_mode) - && (sb.st_mode & S_IWOTH) == 0; - - if (!perms_ok) - l_error("\"%s\" should be a file that is not " - "world writable.", - f); - } else if (errno == ENOENT) { - perms_ok = true; - - l_debug("File \"%s\" does not exist.", f); - } else { - l_debug("Unexpected error during file " - "permissions check."); - } - - return perms_ok; -} - static void parse_config_log(struct mptcpd_config *config, struct l_settings const *settings, char const *group) @@ -643,6 +638,23 @@ static void parse_config_plugins_to_load( set_plugins_to_load(config, plugins_to_load); } +static void parse_config_plugins_conf_dir( + struct mptcpd_config *config, + struct l_settings const *settings, + char const *group) +{ + if (config->plugins_conf_dir != NULL) + return; // Previously set, e.g. via command line. + + char *const plugins_conf_dir = + l_settings_get_string(settings, + group, + "plugins-conf-dir"); + + if (plugins_conf_dir != NULL) + set_plugins_conf_dir(config, plugins_conf_dir); +} + /** * @brief Parse configuration file. * @@ -652,49 +664,33 @@ static void parse_config_plugins_to_load( * @return @c true on successful configuration file parse, and * @c false otherwise. */ -static bool parse_config_file(struct mptcpd_config *config, - char const *filename) +static void parse_config_file(struct l_settings const* settings, + void *user_data) { - assert(filename != NULL); - - if (!check_file_perms(filename)) - return false; - - struct l_settings *const settings = l_settings_new(); - if (settings == NULL) { - l_error("Unable to create mptcpd settings."); + struct mptcpd_config *config = user_data; - return false; - } + static char const group[] = "core"; - if (l_settings_load_from_file(settings, filename)) { - static char const group[] = "core"; + // Logging mechanism. + parse_config_log(config, settings, group); - // Logging mechanism. - parse_config_log(config, settings, group); + // Plugin directory. + parse_config_plugin_dir(config, settings, group); - // Plugin directory. - parse_config_plugin_dir(config, settings, group); + // Notification for existing addresses. + parse_config_notify_flags(config, settings, group); - // Notification for existing addresses. - parse_config_notify_flags(config, settings, group); + // Address flags. + parse_config_addr_flags(config, settings, group); - // Address flags. - parse_config_addr_flags(config, settings, group); + // Default plugin. + parse_config_default_plugin(config, settings, group); - // Default plugin. - parse_config_default_plugin(config, settings, group); - - // Plugins to load. - parse_config_plugins_to_load(config, settings, group); - } else { - l_debug("Unable to load mptcpd settings from file '%s'", - filename); - } + // Plugins to load. + parse_config_plugins_to_load(config, settings, group); - l_settings_free(settings); - - return true; + // Plugins configuration directory. + parse_config_plugins_conf_dir(config, settings, group); } /** @@ -728,7 +724,9 @@ static bool parse_config_files(struct mptcpd_config *config) for (char const *const *file = files; file != end && parsed; ++file) { - parsed = parse_config_file(config, *file); + parsed = mptcpd_config_read(*file, + parse_config_file, + config); } return parsed; @@ -779,6 +777,9 @@ static bool merge_config(struct mptcpd_config *dst, } } + if (dst->plugins_conf_dir == NULL) + dst->plugins_conf_dir = l_strdup(src->plugins_conf_dir); + return true; } @@ -798,6 +799,13 @@ static bool check_config(struct mptcpd_config const *config) return false; } + if (config->plugins_conf_dir == NULL) { + l_error("mptcpd plugins configuration directory was not " + "configured."); + + return false; + } + return true; } @@ -817,7 +825,8 @@ struct mptcpd_config *mptcpd_config_create(int argc, char *argv[]) // System configuration, e.g. /etc/mptcpd/mptcpd.conf. struct mptcpd_config sys_config = { .log_set = NULL }; static struct mptcpd_config const def_config = { - .plugin_dir = MPTCPD_DEFAULT_PLUGINDIR }; + .plugin_dir = MPTCPD_DEFAULT_PLUGINDIR, + .plugins_conf_dir = MPTCPD_DEFAULT_PLUGINSCONFDIR }; char flags[128]; /* @@ -833,6 +842,7 @@ struct mptcpd_config *mptcpd_config_create(int argc, char *argv[]) && merge_config(config, &def_config) && check_config(config); + l_free(sys_config.plugins_conf_dir); l_queue_destroy(sys_config.plugins_to_load, l_free); l_free(sys_config.default_plugin); l_free(sys_config.plugin_dir); @@ -871,6 +881,9 @@ struct mptcpd_config *mptcpd_config_create(int argc, char *argv[]) l_free(str); } + l_debug("plugins configuration directory: %s", + config->plugins_conf_dir); + return config; } @@ -879,13 +892,13 @@ void mptcpd_config_destroy(struct mptcpd_config *config) if (config == NULL) return; + l_free(config->plugins_conf_dir); l_queue_destroy(config->plugins_to_load, l_free); l_free(config->default_plugin); l_free(config->plugin_dir); l_free(config); } - /* Local Variables: c-file-style: "linux" diff --git a/src/path_manager.c b/src/path_manager.c index b5347377..efa6488b 100644 --- a/src/path_manager.c +++ b/src/path_manager.c @@ -660,6 +660,7 @@ static void complete_pm_init(void *data) */ if (!mptcpd_plugin_load(pm->config->plugin_dir, pm->config->default_plugin, + pm->config->plugins_conf_dir, pm->config->plugins_to_load, pm)) { l_error("Unable to load path manager plugins."); diff --git a/tests/test-cxx-build.cpp b/tests/test-cxx-build.cpp index 85905f1e..3a466d20 100644 --- a/tests/test-cxx-build.cpp +++ b/tests/test-cxx-build.cpp @@ -72,7 +72,11 @@ class test_plugin struct mptcpd_pm *const pm = NULL; bool const loaded = - mptcpd_plugin_load(dir, default_plugin, NULL, this->pm); + mptcpd_plugin_load(dir, + default_plugin, + dir, + NULL, + this->pm); assert(loaded); static struct plugin_call_args const args = { diff --git a/tests/test-plugin.c b/tests/test-plugin.c index e32fe301..0546ae3e 100644 --- a/tests/test-plugin.c +++ b/tests/test-plugin.c @@ -43,7 +43,7 @@ static bool run_plugin_load(mode_t mode, struct l_queue const *queue) assert(mode_ok == 0); bool const loaded = - mptcpd_plugin_load(dir, default_plugin, queue, pm); + mptcpd_plugin_load(dir, default_plugin, dir, queue, pm); if (loaded) { static struct plugin_call_args const args = { @@ -121,7 +121,7 @@ static void test_no_plugins(void const *test_data) assert(dir != NULL); struct mptcpd_pm *const pm = NULL; - bool const loaded = mptcpd_plugin_load(dir, NULL, NULL, pm); + bool const loaded = mptcpd_plugin_load(dir, NULL, dir, NULL, pm); (void) rmdir(dir); @@ -202,7 +202,7 @@ static void test_plugin_dispatch(void const *test_data) struct mptcpd_pm *const pm = NULL; bool const loaded = - mptcpd_plugin_load(dir, default_plugin, NULL, pm); + mptcpd_plugin_load(dir, default_plugin, dir, NULL, pm); assert(loaded); // Notice that we call plugin 1 twice. @@ -284,7 +284,11 @@ static void test_null_plugin_ops(void const *test_data) static char const *const default_plugin = NULL; struct mptcpd_pm *const pm = NULL; - bool const loaded = mptcpd_plugin_load(dir, default_plugin, NULL, pm); + bool const loaded = mptcpd_plugin_load(dir, + default_plugin, + dir, + NULL, + pm); assert(loaded); char const name[] = "null ops"; @@ -335,6 +339,7 @@ static void test_null_plugin_dir(void const *test_data) bool const loaded = mptcpd_plugin_load(dir, default_plugin, + dir, plugins_to_load, pm); assert(!loaded); @@ -355,6 +360,7 @@ static void test_bad_plugins(void const *test_data) bool const loaded = mptcpd_plugin_load(dir, default_plugin, + dir, plugins_to_load, pm); assert(!loaded);