(resending with shorter subject line since the original got dropped)
Fix: local_parts = dsearch,filter=dir;MAILMAN_HOME/lists and then untainted
 $local_part_data finally works

The top of the answer is mostly for people who worked on the tainting
code.  I apologize for sending this so late, I now see that it broke me
a long time ago and it was so hard to fix that I reverted exim and went
on with my life, hoping things would get fixed, but they did not.
Turns out I had already hit xkcd 349 back then and made the choice to revert
exim back then.
http://marc.merlins.org/perso/computers/post_2025-10-26_Finishing-Upgrade-of-Year-2000-Linux-System-From-i386-to-amd64-to-arm64-for-Raspberry-Pi5-with-mailman-2_1_7-for-Python-2.html

So, I'll add to my earlier hopefully constructive feedback.
Why, oh why, is there not an untaint() function with a known safe regex?
Why all this complex wizardry to untaint, having to dig into deep exim4
internals for a forced upgrade that literaly breaks you until you find
the fix? (and for a problem that never even ever was a problem in my install,
but exim considered I was too incompetent to be trusted to know myself, so it
forced breakage on me to "help me stay secure")

Seriously
1) add a untaint() with fixed safe regex that will work for most everyone
2) the local_part_data is deep black magic and not a reasonable solution.
there should be a local_part_safe that is automatically populated.
3) the debian answer of "turn off tainting" should honestly be a real option.
Forcing admins to be broken if they have certified they are safe, or in an 
environment where it's really fine, is NOT an appropriate answer and honestly
unfair to admins who deal with lots of things and, cannot be experts on deep
internals of dozens or hundreds of daemons.

For people who disagree with #3, please understand that it is still there
no matter what. If admins cannot untaint a safe config, they will downgrade
exim, and it looks like I did exactly that in the past.

I lost 8H to this and honestly I think a fair amount of admins would
give up earlier, downgrade exim, and pin it to an old version that never
gets upgraded again, a much worse loss for security. 
Update: now it seems that I did exactly that over a year ago and forgot
I had.

Yes, I know I'm super late to make a difference here, but I hope the
relevant folks will still listen and maybe consider a better option now
or in the future, or at the very least this be a case study of a non
optimal way to solve a security problem.
At the very least I hope my plea to make things easier for Email admins
who are likekly juggling 100 other things, will be heard.

The deep black magic that I still barely understand after putting the bits
together, is:
1) local_parts = dsearch,filter=dir;MAILMAN_HOME/lists
2) which in turn populates local_part_data, which is untainted but remains
very much NULL by default without the first command above.

Why is there no safe_local_part that is automatically cleaned up with a
safe regex instead of this black magic?

So I'll paste the full result:
mm21_director:
  debug_print = "R: mm21_director for $local_part@$domain"
  driver = accept
  # black magic to populate local_part_data, the untainted version of local_part
  local_parts = dsearch,filter=dir;MAILMAN_HOME/lists
  require_files = MAILMAN_HOME/lists/${lc::$local_part_data}/config.pck
  local_part_suffix = 
"-bounces:-bounces+*:-confirm+*:-join:-leave:-owner:-request:-admin"
  transport = mm21_transport
.endif

mm21_transport:
  debug_print = "T: mm21_transport for $local_part@$domain"
  driver = pipe
  # In case you wonder, substr_2 removes the leading '-'
  # and the regex removes optional +foo=hostname that can be after -bounce
  # (if you use VERP) -- Marc
  command = MAILMAN_WRAP "${if 
def:local_part_suffix{${substr_2:{${sg{${lc:$local_part_suffix}}{\\\\\+.*}{}}}}{post}}"
 ${lc:$local_part_data}
  current_directory = MAILMAN_HOME
  home_directory = MAILMAN_HOME
  user = MAILMAN_UID
  group = MAILMAN_GID

The very worst part is I now vaguely remember having everything break on
a foolish upgrade due to dependencies a while ago, and after spending
more hours then trying to fix it, and local_part_data expanding to NULL,
I reverted everything and put exim4 back down to a good known version
because it was in the middle of the night by then and I needed sleep.

Only to do it again a long time later, and finally succeeding thanks to
your kind help, but I'm honestly going to remember this experience as battle
scars, more than a victory :-/

On Mon, Oct 27, 2025 at 08:04:42AM +0000, Andrew C Aitchison via Exim-users 
wrote:
> Note that tainting was introduced in one version and
> later exended. You might as well ensure your config works
> with the latest version.
 
Thanks. On the previous post I wrote on "this should be easier", it was
also quite frustrating that of course the bounce Email contains no info,
but not even the exim logs which could show in panic that a delivery was
stopped because of tainting and giving full debug info there.
exim -d+all is not the easiest to use and parse.

> Did you try using $local_part_data ?

thanks for suggestion, it seems to end up empty,not sure why.

09:59:11 3237622  ├───expanded: 
${if░def:local_part_suffix{${substr_2:{${sg{${lc:$local_part_suffix}}{\\+.*}{}}}}{post}}
09:59:11 3237622  ╰─────result: post
09:59:11 3237622 arg 2
09:59:11 3237622  ╭considering: ${lc:$local_part_data}
09:59:11 3237622   ╭considering: $local_part_data}
09:59:11 3237622   ├──────value: 
09:59:11 3237622   ├considering: }
09:59:11 3237622   ├───expanded: $local_part_data
09:59:11 3237622   ╰─────result: 
09:59:11 3237622  ├─────op-res: 
09:59:11 3237622  ├───expanded: ${lc:$local_part_data}
09:59:11 3237622  ╰─────result: 
09:59:11 3237622 direct command after expansion:
09:59:11 3237622   argv[0] = '/var/local/mailman/mail/mailman'
09:59:11 3237622   argv[1] = 'post'
09:59:11 3237622   argv[2] = ''

it got very untainted :)

On Mon, Oct 27, 2025 at 10:32:28AM +0300, Odhiambo Washington wrote:
> Hello Marc,
> 
> Long time!
 
haha, yes indeed. Surprised anyone even remembers me :)

> Here are my MM21 bits (which I commented out when I moved to MM3).
> I had no issues with Exim 4.9x:

oooh, thanks. I wrote mine so long ago that it would likely take me days
of relearning all of exim4 to be able to write my config file again,
never mind a new one.

> mailman_transport:
>   driver             = pipe
>   command      = MAILMAN_WRAP \
>                          '${if def:local_part_suffix \
>                          {${sg{$local_part_suffix}{-(\\w+)(\\+.*)?}{\$1}}} \
>                          {post}}' \
>                          ${lc:$local_part_data}

this is similar enough to mine. We both use local_part_data which is tainted.

  command = MAILMAN_WRAP "${if 
def:local_part_suffix{${substr_2:{${sg{${lc:$local_part_suffix}}{\\\\\+.*}{}}}}{post}}"
 ${lc:$local_part_data}

I'm not sure how or why yours would work. Odds are that it would not anymore.

> PS: Also, If you need any help moving to Mailman3, just hola :)

Super nice to offer. As per my previous message, it would be another bunch of
hours (or day+) to migrate everything I have for just a few small lists that are
barely used anymore. Not really worth the time in my case.
I almost got my mailman2 install to work as-is, even with i386 binaries running
on arm64 (the sgid wrappers), but after a quick native recompile of the wrappers
from a source tree I still had all the way back from 2002 :) , mailman2 came up
working with my local patches and everything.
Except for the hours spent into exim4 after that, which brought us here.

Story of the upgrade, which went surprisingly well for a hand migration
of 25 years of stuff, except for exim:
http://marc.merlins.org/perso/computers/post_2025-10-26_Finishing-Upgrade-of-Year-2000-Linux-System-From-i386-to-amd64-to-arm64-for-Raspberry-Pi5-with-mailman-2_1_7-for-Python-2.html

On Mon, Oct 27, 2025 at 08:12:29AM +0100, Thomas Krichel wrote:
>   Marc MERLIN via Exim-users writes
> 
> > I understand why tainting and the basics, but in the case of the mailman
> > transport I wrote 20+ years ago, it is safe and I'd like to untaint
> > local_part.  
> 
>   You should not be running Mailman21. It is no longer being
>   supported. You need to move on to Mailman3.
> 
>   But I confess: I am a fellow sinner. I run Mailman21 with
> 
> root@darni ~ # aptitude show exim4 | grep Version
> Version: 4.98.2-2

Same here, more or less
ii  exim4-daemon-light 4.98.2-1

>   which I believe is post tainting. So it should be possible to have a

it absolutely is. It's also after the debian option to turn off tainting because
of everything it broke without trivial ways to fix them, was removed.

Thank you for sharing, I see you are using local_part_data, which expands
to null for me. Let me try again. 

Well, it still expands to NULL
Just changing it in here, where it's not needed causes the director not to work

mm21_main_director:
  debug_print = "R: mm21_main_director for $local_part@$domain"
  driver = accept
  # We'll allow listname+foo addressing, but not for other admin addresses
  local_part_suffix = +*
  local_part_suffix_optional 
  require_files = MAILMAN_HOME/lists/${lc::$local_part_data}/config.pck
  transport = mm21_transport

10:37:41 3304111 R: mm21_main_director for [email protected]
10:37:41 3304111 checking require_files
10:37:41 3304111  ╭considering: 
/var/local/mailman/lists/${lc:$local_part_data}/config.pck
10:37:41 3304111  ├───────text: /var/local/mailman/lists/
10:37:41 3304111  ├considering: ${lc:$local_part_data}/config.pck
10:37:41 3304111   ╭considering: $local_part_data}/config.pck
10:37:41 3304111   ├──────value: 
10:37:41 3304111   ├considering: }/config.pck
10:37:41 3304111   ├───expanded: $local_part_data
10:37:41 3304111   ╰─────result: 
10:37:41 3304111  ├─────op-res: 
10:37:41 3304111  ├considering: /config.pck
10:37:41 3304111  ├───────text: /config.pck
10:37:41 3304111  ├───expanded: 
/var/local/mailman/lists/${lc:$local_part_data}/config.pck
10:37:41 3304111  ╰─────result: /var/local/mailman/lists//config.pck

So, I went back to the docs
https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html#vi326
When the local_parts condition on a router or ACL matches a local part
list the match value is copied to $local_part_data.

What I needed was
mm21_main_director:
  debug_print = "R: mm21_main_director for $local_part@$domain"
  driver = accept
  # black magic to get local_part_data populated
  local_parts = dsearch,filter=dir;MAILMAN_HOME/lists
  # We'll allow listname+foo addressing, but not for other admin addresses
  local_part_suffix = +*
  local_part_suffix_optional
  require_files = MAILMAN_HOME/lists/${lc::$local_part_data}/config.pck
  transport = mm21_transport

Thanks a whole bunch with your config bit with the magic
"  local_parts = dsearch,filter=dir;MAILMAN_HOME/lists "
which I realistically would never have found on my own.

Thank you to everyone.
Marc
-- 
"A mouse is a device used to point at the xterm you want to type in" - A.S.R.
 
Home page: http://marc.merlins.org/                       | PGP 7F55D5F27AAF9D08

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

Reply via email to