]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-master: Support validating config filters against requests
authorAki Tuomi <aki.tuomi@dovecot.fi>
Thu, 30 Nov 2017 13:47:25 +0000 (15:47 +0200)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Tue, 6 Mar 2018 10:13:24 +0000 (12:13 +0200)
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.

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

index 59f89a4b2639ccf31630691fc97f8c927c1fd522..7d400cb35152d334f8b10e40575c6dbc875c5e9c 100644 (file)
@@ -4,6 +4,7 @@ noinst_LTLIBRARIES = libmaster.la
 
 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 @@ noinst_PROGRAMS = $(test_programs)
 
 test_libs = \
        ../lib-test/libtest.la \
+       ../lib-dns/libdns.la \
        ../lib/liblib.la
 
 test_deps = $(noinst_LTLIBRARIES) $(test_libs)
index d6164ffb0da35d6982543499eb0b53f5acf5101c..11e92041b5148a94692059f81e80f2ff2fb1db8a 100644 (file)
@@ -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"
 #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 @@ struct master_service_settings_cache {
        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 @@ master_service_settings_cache_init(struct master_service *service,
        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 @@ int master_service_settings_cache_read(struct master_service_settings_cache *cac
                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);
index de94042222c2939fd9e0c526f164b7e370763919..157132e639b9cf69e79df8b0e18542bb015825c0 100644 (file)
@@ -6,7 +6,7 @@ master_service_settings_cache_init(struct master_service *service,
                                   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,
index 92d22e1efc231a8497b05a72c78bdc4fab571951..59d2165e0a3247683fee30d3eae84f394a35502a 100644 (file)
@@ -303,6 +303,18 @@ config_send_request(struct master_service *service,
        return 0;
 }
 
+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,
@@ -399,6 +411,54 @@ void master_service_config_socket_try_open(struct master_service *service)
                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,
index 43e45415d716b62bc65ccbfba314cfac40dd3a25..39ae8b472fff42a20c60e4452f052a4707d20906 100644 (file)
@@ -67,6 +67,9 @@ extern const struct setting_parser_info master_service_setting_parser_info;
 /* 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,
index 506fe2b6d85c97d09ad1a802faa47a0ef6ef8bc3..fdc390dda677cc9b6278edb381d611056d3fec82 100644 (file)
@@ -53,6 +53,14 @@ int master_service_settings_read(struct master_service *service ATTR_UNUSED,
        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)
 {