That lookup thing doesn't seem to help either. Exim is not willing to work anymore. I changed the sieve directory line to do a database lookup of $local_part which just returns the same value effectively. But it remains tainted, according to the debug output.

Here's the config snippets:

SQL_VERIFY_LOCAL = \
        select local \
        from mail_entries \
        where local = lower('${quote_pgsql:$local_part}') and domain = 
lower('${quote_pgsql:$domain}')
>
begin routers
>
virtual_user_filter:
        driver = redirect
        local_part_suffix = +*
        local_part_suffix_optional
        allow_fail
        allow_defer
        allow_filter
        user = Debian-exim
        data = ${lookup pgsql{SQL_FILTER}{$value}}
        address_data = ${lookup pgsql{SQL_QUOTA}{$value}fail}
        file_transport = address_directory
        reply_transport = address_reply
        sieve_useraddress = $local_part
        sieve_subaddress = ${sg{$local_part_suffix}{^.}{}}
        sieve_vacation_directory = /var/mail/sieve/${lookup 
pgsql{SQL_VERIFY_LOCAL}{$value}}@$domain/vacation

When I run this:

exim -d -bt b...@mydomain.de

I get this output:

> (...)
data is a Sieve filter program
Sieve: start of processing
  search_open: pgsql "NULL"
  search_find: file="NULL"
    key="select local from mail_entries where local = lower('box') and domain = 
lower('mydomain.de')" partial=-1 affix=NULL starflags=0 opts=NULL
  LRU list:
  internal_search_find: file="NULL"
    type=pgsql key="select local from mail_entries where local = lower('box') and 
domain = lower('mydomain.de')" opts=NULL
  database lookup required for select local from mail_entries where local = 
lower('box') and domain = lower('mydomain.de')
                               (tainted, quoted:pgsql)
  PostgreSQL query: "select local from mail_entries where local = lower('box') and 
domain = lower('mydomain.de')" opts 'NULL'
  PGSQL new connection: socket=/var/run/postgresql/.s.PGSQL.5432 database=dfctl 
user=xxx
  creating new cache entry
  lookup yielded: box
LOG: MAIN PANIC
  Tainted dirname '/var/mail/sieve/b...@mydomain.de/vacation'
 Sieve error: unable to open vacation directory in line 2
Sieve: end of processing
search_tidyup called
close PGSQL connection: (/var/run/postgresql/.s.PGSQL.5432)/dfctl/xxx
Exim pid=4977 (router-interpret) terminating with rc=0 >>>>>>>>>>>>>>>>
rda_interpret: subprocess yield=0 error=NULL
set transport address_directory
virtual_user_filter router generated inbox
  pipe, file, or autoreply
  errors_to=NULL transport=address_directory
  uid=119 gid=119 home=NULL
routed by virtual_user_filter router
  envelope to: b...@mydomain.de
  transport: <none>

Considering inbox
b...@mydomain.de -> inbox
  transport = address_directory
search_tidyup called
Exim pid=4975 (fresh-exec) terminating with rc=0 >>>>>>>>>>>>>>>>

There's a LOG: MAIN PANIC in there and the line following it doesn't sound good.

So if the local_part cannot be detainted by a database lookup, then how is that thing supposed to work at all?

-Yves



-------- Ursprüngliche Nachricht --------
Von: Jeremy Harris via Exim-users <exim-users@lists.exim.org>
Datum: 2025-05-05 23:02 +02:00
Betreff: [exim] Re: Upgrading Exim to 4.94: $local_part vs. $local_part_data

On 2025/05/05 9:25 PM, Yves Goergen via Exim-users wrote:

This router, however, is only used if the local_part has been successfully looked up by a prior SQL query. So by the time we're here, I know that the value is valid.

What I'm not sure about is whether I can use the variable or should use the same value from the database lookup instead.

Nope.

While you may know this, Exim does not - and has mechanics that stop you:
a string resulting from an expansion that uses tainted data
(here, both $local_part and $domain - having been supplied by a potential
attacker, they are not to be trusted) is also labelled as being tainted.

And then, a tainted value may not be further expanded (because that is an
attack route, cf. log4j).  Using it as a file path counts as expansion.


So here's the usual method for detainting for a filename context:
we use the existing filesystem as the local, trusted, database
and we do a lookup in that database using the tainted data as a key.

sieve_vacation_directory = ${lookup {$local_part@$domain} \
                 dsearch,ret=full \
                 {/var/mail/sieve}}/vacation

--
## subscription configuration (requires account):
##   https://lists.exim.org/mailman3/postorius/lists/exim-users.lists.exim.org/
## unsubscribe (doesn't require an account):
##   exim-users-unsubscr...@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/

Reply via email to