Am 2012-09-19 22:27, schrieb Wietse Venema:
Michael Storz:
The consistency check requires that a user object is first (correctly) defined in OpenLDAP. Only then the second check looks for the correct definition in Active Directory. If it is not then we defer the email (we
...
If a new user is created the user object appears instantly in the
OpenLDAP directory. [...]
Therefore if the user/address is not in the OpenLDAP directory I can
sa[f]ely reject such an address.

So it is an inconsistency between LDAP and Active Directory, such
that the user exists in LDAP but not yet in Active Directory.

It's almost like different organizations being responsible for
different parts of the infrastructure. Hm.

We can attack this in a number of ways.

- You want to return "450 User not yet available" when the query
  returns NOTFOUND. This could be as simple as exploiting the linear
  search order of smtpd_mumble_restrictions:

    smtp_mumble_restrictions =
        ...
        # Query AD first.
        check_something_access ldap:the-AD-table
        # Not found in AD, return a generic error.
        check_something_access pcre:/etc/postfix/mumble_default

    /etc/postfix/mumble_default
        /./ 450 User not yet available

- Introduce special lookup keys for each table-driven feature
  (access, transport_maps, canonical, etc.). The "*" in transports
  is a particularly ugly example. If it would be done in a more
  general manner (see examples below), then I would not have to
  maintain special code that works only for one feature.

- Introduce new table syntax for multi-table features:

    local_recipient_maps =
        ldap:the-AD-table
        notfound=tempfail:

  Where tempfail is a map that always fails all queries with a
  temporary error.

  This requires changing all code that queries multiple maps, so
  that it makes an extra call to pick up the "notfound" result.
  That's pretty straightforward, as long as we resist the temptation
  to extend "notfound" and make it into a programming language.

- Finer control over multi-table iteration.  If there were a way
  to make Postfix do all the queries on the first map before it
  queries the second map, and so on, then that could also solve the
  problem.

It seems that your problem is not with the multi-table features,
so the linear smtpd_mumble_restrictions search order should take
care of things.

        Wietse

Hi Wietse,

thank you for helping me to solve my problem. I tried to present a simple example to show my problem. Unfortunately, reality is much more complex. In this case 3 IDMs, 3 different directories (MS AD, OpenLDAP, Novell eDir) and 3 email systems which share mail domains are involved. Therefore the consistency checks are a little bit more complex than the one I described. Because it seems I was
not able to show the underlying problem, let me rephrase my problem:

        How can I configure arbitrarily complex if-then-else constructs
        for every possible table type?

I can see how to configure special cases, but I am stuck with a general solution to this problem. To make it easier to show me where my conclusions are wrong,
let me show my reasoning step by step (sorry for this lengthy posting).

First let me revisit restriction classes and access table actions. As far as I understand it, there are 2 all encompassing system restriction classes. For receiving an email a class which I will call default_restriction_class to have
a name for it:

default_restriction_class =
    smtpd_client_restrictions
    smtpd_helo_restrictions
    smtpd_sender_restrictions
    smtpd_recipient_restrictions
    smtpd_data_restrictions
    smtpd_end_of_data_restrictions

and a special class for the commnd ETRN:

etrn_restriction_class =
    smtpd_client_restrictions
    smtpd_helo_restrictions
    smtpd_etrn_restrictions

Every action which can be specified on the RHS in an access table has two parts: the actual action and a control for the flow of the evaluation of restrictions (jump type). All actions can be grouped into 3 categories of jump types:

DUNNO:
------
DUNNO jumps to the end of the current evaluation of an access table. Further
processing is done with the next restriction.

actions: BCC, DEFER_IF_PERMIT, DEFER_IF_REJECT, (DISCARD),
         DUNNO, FILTER, HOLD, PREPEND, REDIRECT, WARN

OK:
___
OK jumps to the end of the current smtpd_mumble_restrictions class. At that point it is converted to DUNNO. Further processing is done with the next smtpd_mumble_restrictions class from either default_restriction_class or
etrn_restriction_class.

actions: (DISCARD), OK, PERMIT, RELAY, all numeric code

REJECT:
-------
REJECT (in an abstract sense) jumps to the end of the default_restriction_class or the etrn_restriction_class. No further restriction or restriction class is
evaluated.

actions: DEFER, REJECT, 4XX, 5XX

Let me come to the if-then-else constructs. The simplest one is

---------------------------------------------------------------------
if (condition) {
    restriction1;
    restriction2;
} else {
    DUNNO
}
---------------------------------------------------------------------

which evaluates the listed restrictions if the condition is true (key found). This is the way all check_mumble_access commands work and every kind of table
can be used for it:

---------------------------------------------------------------------
/etc/postfix/main.cf:
    smtpd_mumble_restrictions =
        ...
        check_mumble_access table_type:/etc/postfix/condition
        ...

e.g. table_type=hash
/etc/postfix/condition:
key1   restriction1 restriction2
---------------------------------------------------------------------
However, if we have in addition an else-clause it gets difficult:

---------------------------------------------------------------------
if (condition) {
    restriction1;
    restriction2;
} else {
    restriction3;
    restriction4;
}
---------------------------------------------------------------------
If we can use a regexp or pcre table, we can use the default value of
/./ for the else-clause:

---------------------------------------------------------------------
/etc/postfix/main.cf:
    smtpd_mumble_restrictions =
        ...
        check_mumble_access pcre:/etc/postfix/condition
        ...

/etc/postfix/condition:
/^key1$/  restriction1 restriction2
/./       restriction3 restriction4
---------------------------------------------------------------------
Or if we can use a cidr table, we can use the default value of
0.0.0.0/0 for the else-clause:

---------------------------------------------------------------------
/etc/postfix/main.cf:
    smtpd_mumble_restrictions =
        ...
        check_client_mumble_access cidr:/etc/postfix/condition
        ...

/etc/postfix/condition:
key1       restriction1 restriction2
0.0.0.0/0  restriction3 restriction4
---------------------------------------------------------------------
All other access tables do not have a default. However there is still another special case. When the last restriction of the if-clause (here restriction2) always returns an action of type REJECT or OK, but never of type DUNNO we
can use:

---------------------------------------------------------------------
/etc/postfix/main.cf:
    smtpd_restriction_classes =
        condition

    condition =
        # if
        check_mumble_access table_type:/etc/postfix/condition
        # else
        restriction3
        restriction4

    smtpd_mumble_restrictions =
        ...
        condition
        ...

e.g. table_type=hash
/etc/postfix/condition:
key1   restriction1 restriction2
---------------------------------------------------------------------
However, if the condition is true, no other restriction after the if-then-else construct will be evaluated in this smtpd_mumble_restrictions. Therfore this is not a general solution. And even worse, if the last restriction in the if-clause evaluates (sometimes) to DUNNO, then I do not see how this can be configured. At this point I am stuck and need your help. Is it possible to
configure such a construct with Postfix?

If not, may I suggest the following solution. I would introduce of a 4th jump type, which I will call LAST. It would stand between DUNNO and OK. It jumps to the end of the current restriction class, either user or system defined. At that point it is converted to DUNNO. Further processing is done with the next restriction after the current restriction class (do you see the similarity to
OK?). With this action we can configure the following:

---------------------------------------------------------------------
/etc/postfix/main.cf:
    smtpd_restriction_classes =
        condition

    condition =
        # if
        check_mumble_access table_type:/etc/postfix/condition
        # else
        restriction3
        restriction4

    smtpd_mumble_restrictions =
        ...
        condition
        ...

e.g. table_type=hash
/etc/postfix/condition:
key1   restriction1 restriction2 LAST
---------------------------------------------------------------------
If the condition is true and restriction1 and restriction2 evaluated to jump type DUNNO the action LAST will be executed. This would jump over restriction3
and restriction4 to the end of restriction class condition and the next
following restriction would be evaluated. This means the general if-then-else
construction could be build with Postfix restriction classes.

If we have a long list of restrictions for the if-clause it would be nice to be able to encapsulate the list in an own restriction class. In this case we would need a label for the LAST action with the name of the outer restriction class
to leave:

---------------------------------------------------------------------
nice general case: LAST with label

/etc/postfix/main.cf:
    smtpd_restriction_classes =
        condition
        if_restriction_list

    condition =
        # if
        check_mumble_access table_type:/etc/postfix/condition
        # else
        restriction3
        restriction4

    if_restriction_list =
        restriction1
        restriction2
        LAST condition

    smtpd_mumble_restrictions =
        ...
        condition
        ...

e.g. table_type=hash
/etc/postfix/condition:
key1   if_restriction_list
---------------------------------------------------------------------
As you can see LAST works the same way for user restriction classes as OK works
for smtpd_mumble_restrictions classes.

Does this make sense now? Or did I overlook something?

Michael

Reply via email to