changeset 22878:6b75bab3ec79

lib-master: Support validating config filters against requests Validation will sanitize the input request and drop any fields that have no filter in config. E.g. if you have a local block with name, and nothing else, then lip/rip will be dropped from the request.
author Aki Tuomi <aki.tuomi@dovecot.fi>
date Thu, 30 Nov 2017 15:47:25 +0200
parents 017c83019f41
children 685a613c315f
files src/lib-master/Makefile.am src/lib-master/master-service-settings-cache.c src/lib-master/master-service-settings-cache.h src/lib-master/master-service-settings.c src/lib-master/master-service-settings.h src/lib-master/test-master-service-settings-cache.c
diffstat 6 files changed, 164 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-master/Makefile.am	Thu Nov 30 15:46:52 2017 +0200
+++ b/src/lib-master/Makefile.am	Thu Nov 30 15:47:25 2017 +0200
@@ -4,6 +4,7 @@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-dns \
 	-I$(top_srcdir)/src/lib-test \
 	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-ssl-iostream \
@@ -58,6 +59,7 @@
 
 test_libs = \
 	../lib-test/libtest.la \
+	../lib-dns/libdns.la \
 	../lib/liblib.la
 
 test_deps = $(noinst_LTLIBRARIES) $(test_libs)
--- a/src/lib-master/master-service-settings-cache.c	Thu Nov 30 15:46:52 2017 +0200
+++ b/src/lib-master/master-service-settings-cache.c	Thu Nov 30 15:47:25 2017 +0200
@@ -1,9 +1,11 @@
 /* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "wildcard-match.h"
 #include "hash.h"
 #include "llist.h"
 #include "settings-parser.h"
+#include "dns-util.h"
 #include "master-service-private.h"
 #include "master-service-settings.h"
 #include "master-service-settings-cache.h"
@@ -12,6 +14,14 @@
 #define CACHE_INITIAL_ENTRY_POOL_SIZE (1024*16)
 #define CACHE_ADD_ENTRY_POOL_SIZE 1024
 
+struct config_filter {
+	struct config_filter *prev, *next;
+
+	const char *local_name;
+	struct ip_addr local_ip, remote_ip;
+	unsigned int local_bits, remote_bits;
+};
+
 struct settings_entry {
 	struct settings_entry *prev, *next;
 
@@ -41,6 +51,8 @@
 	HASH_TABLE(char *, struct settings_entry *) local_name_hash;
 	HASH_TABLE(struct ip_addr *, struct settings_entry *) local_ip_hash;
 
+	struct config_filter *filters;
+
 	/* Initial size for new settings entry pools */
 	size_t approx_entry_pool_size;
 	/* number of bytes malloced by cached settings entries
@@ -70,6 +82,78 @@
 	return cache;
 }
 
+int master_service_settings_cache_init_filter(struct master_service_settings_cache *cache)
+{
+	const char *const *filters;
+	const char *error;
+
+	if (cache->filters != NULL)
+		return 0;
+	if (master_service_settings_get_filters(cache->service, &filters, &error) < 0) {
+		i_error("master-service: cannot get filters: %s", error);
+		return -1;
+	}
+
+	/* parse filters */
+	while(*filters != NULL) {
+		const char *const *keys = t_strsplit_spaces(*filters, " ");
+		struct config_filter *filter =
+			p_new(cache->pool, struct config_filter, 1);
+		while(*keys != NULL) {
+			if (strncmp(*keys, "local-net=", 10) == 0) {
+				(void)net_parse_range((*keys)+10,
+					&filter->local_ip, &filter->local_bits);
+			} else if (strncmp(*keys, "remote-net=", 11) == 0) {
+				(void)net_parse_range((*keys)+11,
+					&filter->remote_ip, &filter->remote_bits);
+			} else if (strncmp(*keys, "local-name=", 11) == 0) {
+				filter->local_name = p_strdup(cache->pool, (*keys)+11);
+			}
+			keys++;
+		}
+		DLLIST_PREPEND(&cache->filters, filter);
+		filters++;
+	}
+	return 0;
+}
+
+/* Remove any elements which there is no filter for */
+static void
+master_service_settings_cache_fix_input(struct master_service_settings_cache *cache,
+				        const struct master_service_settings_input *input,
+					struct master_service_settings_input *new_input)
+{
+	bool found_lip, found_rip, found_local_name;
+
+	found_lip = found_rip = found_local_name = FALSE;
+
+	struct config_filter *filter = cache->filters;
+	while(filter != NULL) {
+		if (filter->local_bits > 0 &&
+		    net_is_in_network(&input->local_ip, &filter->local_ip,
+				      filter->local_bits))
+			found_lip = TRUE;
+		if (filter->remote_bits > 0 &&
+		    net_is_in_network(&input->remote_ip, &filter->remote_ip,
+				      filter->remote_bits))
+			found_rip = TRUE;
+		if (input->local_name != NULL && filter->local_name != NULL &&
+		    dns_match_wildcard(input->local_name, filter->local_name))
+			found_local_name = TRUE;
+		filter = filter->next;
+	};
+
+	*new_input = *input;
+
+	if (!found_lip)
+		i_zero(&new_input->local_ip);
+	if (!found_rip)
+		i_zero(&new_input->remote_ip);
+	if (!found_local_name)
+		new_input->local_name = NULL;
+}
+
+
 void master_service_settings_cache_deinit(struct master_service_settings_cache **_cache)
 {
 	struct master_service_settings_cache *cache = *_cache;
@@ -273,6 +357,12 @@
 		return 0;
 
 	new_input = *input;
+	if (cache->filters != NULL) {
+		master_service_settings_cache_fix_input(cache, input, &new_input);
+		if (cache_find(cache, &new_input, parser_r))
+			return 0;
+	}
+
 	if (dyn_parsers != NULL) {
 		settings_parser_dyn_update(cache->pool, &new_input.roots,
 					   dyn_parsers);
--- a/src/lib-master/master-service-settings-cache.h	Thu Nov 30 15:46:52 2017 +0200
+++ b/src/lib-master/master-service-settings-cache.h	Thu Nov 30 15:47:25 2017 +0200
@@ -6,7 +6,7 @@
 				   const char *module,
 				   const char *service_name);
 void master_service_settings_cache_deinit(struct master_service_settings_cache **cache);
-
+int master_service_settings_cache_init_filter(struct master_service_settings_cache *cache);
 int master_service_settings_cache_read(struct master_service_settings_cache *cache,
 				       const struct master_service_settings_input *input,
 				       const struct dynamic_settings_parser *dyn_parsers,
--- a/src/lib-master/master-service-settings.c	Thu Nov 30 15:46:52 2017 +0200
+++ b/src/lib-master/master-service-settings.c	Thu Nov 30 15:47:25 2017 +0200
@@ -304,6 +304,18 @@
 }
 
 static int
+config_send_filters_request(int fd, const char *path, const char **error_r)
+{
+	int ret;
+	ret = write_full(fd, CONFIG_HANDSHAKE"FILTERS\n", strlen(CONFIG_HANDSHAKE"FILTERS\n"));
+	if (ret < 0) {
+		*error_r = t_strdup_printf("write_full(%s) failed: %m", path);
+		return -1;
+	}
+	return 0;
+}
+
+static int
 master_service_apply_config_overrides(struct master_service *service,
 				      struct setting_parser_context *parser,
 				      const char **error_r)
@@ -399,6 +411,54 @@
 		service->config_fd = fd;
 }
 
+int master_service_settings_get_filters(struct master_service *service,
+					const char *const **filters,
+					const char **error_r)
+{
+	struct master_service_settings_input input;
+	int fd;
+	bool retry = TRUE;
+	const char *path = NULL;
+	ARRAY_TYPE(const_string) filters_tmp;
+	t_array_init(&filters_tmp, 8);
+	i_zero(&input);
+
+	if (getenv("DOVECONF_ENV") == NULL &&
+	    (service->flags & MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS) == 0) {
+		retry = service->config_fd != -1;
+		for (;;) {
+			fd = master_service_open_config(service, &input, &path, error_r);
+			if (fd == -1) {
+				return -1;
+			}
+			if (config_send_filters_request(fd, path, error_r) == 0)
+				break;
+
+			i_close_fd(&fd);
+			if (!retry)
+				return -1;
+			retry = FALSE;
+		}
+		service->config_fd = fd;
+		struct istream *is = i_stream_create_fd(fd, (size_t)-1, FALSE);
+		const char *line;
+		/* try read response */
+		while((line = i_stream_read_next_line(is)) != NULL) {
+			if (*line == '\0')
+				break;
+			if (strncmp(line, "FILTER\t", 7) == 0) {
+				line = t_strdup(line+7);
+				array_append(&filters_tmp, &line, 1);
+			}
+		}
+		i_stream_unref(&is);
+	}
+
+	array_append_zero(&filters_tmp);
+	*filters = array_idx(&filters_tmp, 0);
+	return 0;
+}
+
 int master_service_settings_read(struct master_service *service,
 				 const struct master_service_settings_input *input,
 				 struct master_service_settings_output *output_r,
--- a/src/lib-master/master-service-settings.h	Thu Nov 30 15:46:52 2017 +0200
+++ b/src/lib-master/master-service-settings.h	Thu Nov 30 15:47:25 2017 +0200
@@ -67,6 +67,9 @@
 /* Try to open the config socket if it's going to be needed later by
    master_service_settings_read*() */
 void master_service_config_socket_try_open(struct master_service *service);
+int master_service_settings_get_filters(struct master_service *service,
+					const char *const **filters,
+					const char **error_r);
 int master_service_settings_read(struct master_service *service,
 				 const struct master_service_settings_input *input,
 				 struct master_service_settings_output *output_r,
--- a/src/lib-master/test-master-service-settings-cache.c	Thu Nov 30 15:46:52 2017 +0200
+++ b/src/lib-master/test-master-service-settings-cache.c	Thu Nov 30 15:47:25 2017 +0200
@@ -53,6 +53,14 @@
 	return 0;
 }
 
+int master_service_settings_get_filters(struct master_service *service ATTR_UNUSED,
+					const char *const **filters ATTR_UNUSED,
+					const char **error_r ATTR_UNUSED)
+{
+	return -1;
+}
+
+
 const struct master_service_settings *
 master_service_settings_get(struct master_service *service ATTR_UNUSED)
 {