#1068478 dovecot-core: Wildcard !include statements fail if nothing matches

Package:
dovecot-core
Source:
dovecot-core
Description:
secure POP3/IMAP server - core files
Submitter:
Einhard Leichtfuß
Date:
2024-04-18 16:33:02 UTC
Severity:
normal
Tags:
#1068478#5
Date:
2024-04-05 22:04:29 UTC
From:
To:
Dear Maintainer,

when the Dovecot configuration contains an `!include` statement with a
wildcard that does not match anything, dovecot prints an error and
terminates.

Expected behaviour: Dovecot processes the configuration as if the
`!include` statement was not present.

The upstream on-line documentation [0] says on `!include`:

Steps to reproduce:

  mkdir dir
  printf '%s\n' '!include dir/*.conf' > dovecot.conf
  dovecot -Fc dovecot.conf
  doveconf -c dovecot.conf

Output of `dovecot -Fc doveconf.conf`:

  doveconf: Fatal: Error in configuration file dovecot.conf line 1: No matches

Output of `doveconf -c dovecot.conf`:

  # 2.3.21 (47349e2482): dovecot.conf
  # Pigeonhole version 0.5.21 (f6cd4b8e)
  doveconf: Fatal: Error in configuration file dovecot.conf line 1: No matches

Workaround 1:
 * Create an empty dummy configuration file in `dir/`.

Workaround 2:
 * Use `!include_try` instead.
   * Unlike `!include`, this also silently ignores read errors.

Possibly related upstream mailing list thread:
 * <https://dovecot.org/mailman3/hyperkitty/list/dovecot@dovecot.org/message/S52QEUHS2T6G3FGAYYXIE32YAP7NYGT2/>
 * <https://dovecot.org/mailman3/hyperkitty/list/dovecot@dovecot.org/message/BUSWILJK4DMCZ3NXWYOEWH2ZR2NTEFLA/>

#1068478#10
Date:
2024-04-16 20:51:17 UTC
From:
To:
Control: tags -1 + confirmed upstream
Control: severity -1 minor
claim that this should not result in an error, but that's not consistent
with the code.  Some relevant snippets of code from
src/config/config-parser.c:

from config_parse_line():
        if (strcmp(key, "!include") == 0)
                return CONFIG_LINE_TYPE_INCLUDE;
        if (strcmp(key, "!include_try") == 0)
                return CONFIG_LINE_TYPE_INCLUDE_TRY;

This return value is later handled with a case statement in config_parser_apply_line():
        case CONFIG_LINE_TYPE_INCLUDE:
        case CONFIG_LINE_TYPE_INCLUDE_TRY:
                (void)settings_include(ctx, fix_relative_path(value, ctx->cur_input),
                                       type == CONFIG_LINE_TYPE_INCLUDE_TRY);
                break;

The result of the "type == CONFIG_LINE_TYPE_INCLUDE_TRY" statement is
passed as the bool ignore_errors parameter to bool ignore_errors(), so
if it evaluates to false as it does when type ==
CONFIG_LINE_TYPE_INCLUDE, then we return an error:

        case GLOB_NOMATCH:
                if (ignore_errors)
                        return 0;
                ctx->error = "No matches";
                return -1;

I will pass this along to upstream.  It's not clear from here whether
the issue is with the code or with the documentation.

noah