I've had a look at this myself today.
As was said above, the problem on LDAP is that:
1. The "+" cannot be stripped from LDAP lookups easily, so the catchall eventually succeeds.
2. Postfix will try user+extension@domain on the lookups first, then user@domain after that, but before the catchall for user+extension@domain succeeds, it never tries the second pass.
Possible solutions include stripping out extensions before doing the lookup - but that's probably going to obfuscate delivery, as the headers might get rewritten to hide the extension. Alternatively, if one could change the virtual_alias_maps list between iterations of lookup, that'd solve it (first pass without catchall, second pass including catchall). However, I don't think that's possible either.
Actually, a final option would be not to include a catchall lookup at all. Postfix, will hit the alias_maps in the following order normally:
user@domain, user, @domain
and with extension:
user+ext@domain, user@domain, user+ext, user, @domain
So surely if the LDAP entries for catchalls had similar service enablement and object classes to aliases, the normal postfix lookup process would work for plus addressing and for catchall?
The key differences I spot are, for catchall:
query_filter = (&(objectClass=mailUser)(accountStatus=active)(|(mail=@%d)(shadowAddress=@%d)))
result_attribute= mailForwardingAddress
and for alias:
query_filter = (&(|(mail=%s)(shadowAddress=%s))(accountStatus=active)(enabledService=mail)(enabledService=deliver)(|(objectClass=mailAlias)(&(objectClass=mailUser)(enabledService=forward))))
result_attribute= mailForwardingAddress
So some careful re-wording of that alias filter might be required. Or maybe there's a different map lookup that I'm missing, that would be a better candidate?