Dan Poirier wrote:

I like the idea of replacing ON with AND and OR.  It would not
only provide more control, but make it explicit what kind of merging
was going to happen.

I have mixed thoughts about changing the default to OFF.
Cons: That would mean every container directive would have to specify
some sort of access control (or at least AuthzMergeRules AND) or it'd
be wide open, right?

  I don't think so; at least, that's not what I was intending.
Rather, something much like 2.2's behaviour: containers that don't
specify any authz are simply protected by the "nearest" container
merged ahead of them that does specify authz.


  I'm hoping to put this thread to bed shortly with the patches
available here:

http://people.apache.org/~chrisd/patches/httpd_authnz_configs/

  My intent is to finish up the necessary documentation changes
and get everything committed to trunk in the next few days.  (Fingers
crossed!)

  In the meantime, an overview follows.  Many, many thanks are
due to Brad Nicholes, whose massive refactoring of the authn/z
system makes all of this work possible.


1) <Limit> and <LimitExcept> are made nestable, with an error in
  the case where all methods are configured out.  There are also some
  tuneups related to <Limit>/<LimitExcept> being intended to contain
  authz configurations only and to not be functional outside <Directory>/
  <Location>/etc.

2) A setting of "AuthType None" is allowed, which sets ap_auth_type() to
  NULL and thus provides a way to turn off authentication for a
  sub-directory.  This corresponds to several convenient ways in 2.4 to
  turn off authorization, including "Require all granted" (and, at a
  deeper level, the new "SatisfySections Off").

3) The mod_authz_core.c module is rewritten to attempt to deal with the
  issues discussed on this thread and the previous one, as well as
  those described at the end of this email.  The authz_provider_list
  two-pronged linked lists are replaced by a tree structure that mirrors
  what is configured via <SatisfyAll> and <SatisfyAny>.

  A pair of negative authz containers are introduced, <SatisfyNotAll>
  and <SatisfyNotAny>, which negate their operands in the same
  manner as Reject.  Thus we have the following table:

     Require                    A
     Reject                     !A
     <SatisfyAll>         (A && B && ...)
     <SatisfyAny>              (A || B || ...)
     <SatisfyNotAll>           !(A && B && ...)
     <SatisfyNotAny>           !(A || B || ...)

  The <SatisfyAny> directive is renamed from <SatisfyOne> so as not
  to imply XOR-like functionality (requiring exactly one successful
  operand).

  A number of configuration-time checks are implemented to warn
  administrators regarding redundant or non-functional authz
  configurations. In particular, since the negative authz directives
  can not contribute meaningfully to OR-like blocks, as they
  can only supply neutral (AUTHZ_NEUTRAL) or false (AUTHZ_DENIED)
  values, they are simply not allowed in these containers.  (The
  code should support them, though, if this check is ever removed.)
  Similarly, AND-like blocks without only negative authz directives
  also produce a configuration-time error.

  The MergeAuthzRules directive is renamed SatisfySections and
  take three possible values, Off, All, and And.  The default is Off,
  meaning that as directory configuration sections are merged,
  new authz configurations replace previously merged ones.  However,
  a directory section may specify "SatisfySections All" to force
  its predecessor's authz to be successful as well as its own.
  The "SatisfySections Any" option permits either the predecessor
  or current section's authz to grant the user access.  Note that
  the setting of SatisfySections continues to be local only to
  the directory section it appears in; it is not inherited to
  subsequent sections as they are merged.

  The default setting of SatisfySections is Off, corresponding to
  traditional pre-2.4 authz logic.  Within a directory section,
  the default logic corresponds to an AND-like block (i.e., <SatisfyAll>),
  which differs from the pre-2.4 logic whereby the first Require
  statement to succeed authorized the request.

  Legacy 2.2 configurations should, I hope, work with few or no
  changes as a result of these revisions.  Few administrators, I hope,
  have configurations with multiple Require directives in a section; e.g.:

     <Directory /foo>
         Require group shirt
         Require group shoes
     </Directory>

  If they do, these would need to be revised to either place all the
  items in a single Require directive (e.g., Require group shirt shoes)
  or to use a <SatisfyAny> section.  I feel this makes the overall
  intent of the configuration directives clearer, since it is not
  apparent that the example above grants access to members of either
  group, not just those who are members of both.

  It also means that the following 2.4-style configuration makes
  intuitive sense, because the negative Reject directive only has
  meaning in an AND-like context:

     <Directory /foo>
         Require group shirt
         Reject user noshoes
     </Directory>

  However, if this proves to be a point of considerable difficulty
  for people upgrading to 2.4, it is straightforward to make the
  default logic of a section be OR-like by editing the
  create_default_section() function in mod_authz_core.c.

  The legacy Satisfy directive's logic is already largely handled in
  request.c in 2.4, so mod_access_compat.c could be simplified slightly.

  The <Limit> and <LimitExcept> directives are handled by
  tracking the currently applicable set of methods for all Require/Reject
  directives within a section and its sub-sections.  When the
  request's method does not apply to an authz section, we can then
  immediately return either AUTHZ_GRANTED or AUTHZ_NEUTRAL, depending
  on whether we are in an AND-like or OR-like context, respectively.

4) The mod_authn_default.c and mod_authz_default.c modules are removed,
  shifting the small amount of remaining functionality they provide into
  mod_authn_core.c and mod_authz_core.c.  The existence of both "core"
  and "default" authn/z modules appeared likely to be a source of some
  confusion (especially since they can all be made optional at runtime).

  The mod_authz_default.c module, in particular, was also almost
  entirely obviated by the mod_authz_core.c rewrite, and removing
  it clarified the code paths in many cases where administrators
  likely failed to create a sensible configuration, e.g., by engaging
  authentication with an AuthType directive but configuring not
  authorization to match.


  That's about it for now; comments welcome.  Just for historical
purposes, some things I ran into with the authz_provider_list
two-pronged linked lists with prompted the rewrite:

  First, there were some configurations which could send the
check_provider_list() function into an infinite loop.  For example, with
the following configuration, if one authenticated as user "foo" (i.e.,
a user not in any of the Require directives), the function recursed until
it reached the end of the list(s), received AUTHZ_DENIED from the last
invocation, and then the second-last invocation would loop on the goto
one_next statement.  Since nothing had changed, it just repeated the final
invocation over and over.

AuthzMergeRules Off
<SatisfyOne>
 Require user who
 <SatisfyOne>
   Require user tar
 </SatisfyOne>
 Require user dis
</SatisfyOne>


  Second, the two-pronged authz_provider_list lists seemed to lead to
incorrect authorization behaviour when directives were moved around in
the configuration file in innocuous ways.  For example, with the following
configuration, logging in as user "foo" would succeed:

Require valid-user
<SatisfyOne>
Require user who
Require user foo
</SatisfyOne>

  Moving the first statement to the end of the configuration caused
the user to be rejected, though:

<SatisfyOne>
Require user who
Require user foo
</SatisfyOne>
Require valid-user

  The difference was that in the first case, the linked lists looked
roughly like this:

"Require valid-user"
 req_state =  AUTHZ_REQSTATE_ALL
 all_next  -> "Require user who"
                req_state =  AUTHZ_REQSTATE_ONE
                one_next  -> "Require user foo"
                               req_state = AUTHZ_REQSTATE_ONE

while in the second case, they looked like this:

"Require user who"
 req_state =  AUTHZ_REQSTATE_ONE
 all_next  -> "Require valid-user"
                req_state =  AUTHZ_REQSTATE_ALL
                all_next  -> "Require user foo"
                               req_state = AUTHZ_REQSTATE_ONE

  When check_provider_list() received AUTHZ_DENIED after testing
user "foo" against "Require user who", it found a NULL one_next
pointer and just returned AUTHZ_DENIED, rather than proceeding
any further.

Chris.

--
GPG Key ID: 366A375B
GPG Key Fingerprint: 485E 5041 17E1 E2BB C263  E4DE C8E3 FA36 366A 375B


Reply via email to