Bug 60946

Summary: "Require not env" generates error
Product: Apache httpd-2 Reporter: Philip Prindeville <philipp>
Component: mod_authz_hostAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: NEW ---    
Severity: normal CC: humbedooh, jchampion, philipp, wrowe
Priority: P2    
Version: 2.4.25   
Target Milestone: ---   
Hardware: PC   
OS: Linux   

Description Philip Prindeville 2017-03-31 00:51:35 UTC
If you can have an affirmative test like:

<Location />
    Require env is_good_request
</Location>

you should also be able to have:

<Location />
    Require not env is_bad_request
</Location>

but apparently this isn't allowed.  It results in the error:

Mar 28 14:04:49 mail httpd[2964]: AH00526: Syntax error on line 81 of /etc/httpd/conf.d/mod_setenvif.conf:
Mar 28 14:04:49 mail httpd[2964]: negative Require directive has no effect in <RequireAny> directive

which for many is a meaningless message.

Getting security right should be as painless and straightforward as possible.  Unfortunately, that's not the case here.

I had to instead do:

<Location />
    <RequireAll>
        Require all granted
        Require not env is_bad_request
    </RequireAll>
</Location>

but this, again, is less than obvious.  So I ended up rewriting my configuration as a positive assertion, but that too is awkward:

SetEnvIfExpr true is_good_request=1

BrowserMatch "^the beast$" !is_good_request
...

SetEnvIf GEOIP_COUNTRY_CODE CN !is_good_request
...

SetEnvIf GEOIP_ISP "OHV Hosting" !is_good_request
...

<Location />
    Require env is_good_request
</Location>

instead.  So no matter how you try to write it as a workaround, it's going to have some warts.
Comment 1 Philip Prindeville 2017-03-31 00:55:31 UTC
See also bug 53069.
Comment 2 Christophe JAILLET 2017-03-31 17:01:39 UTC
When dealing with negative condition, corresponding code has the following comment:

            /* For negated directives, if the original result was denied
             * then the new result is neutral since we can not grant
             * access simply because authorization was not rejected.
             */

(see http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authz_core.c?revision=1756038&view=markup&sortby=date#l792)

So, this done on purpose.
Comment 3 Philip Prindeville 2017-03-31 18:02:17 UTC
(In reply to Christophe JAILLET from comment #2)
 
> So, this done on purpose.

Okay, so it's a feature and not a bug.  ;-)

It's still counter-intuitive and more than a little confusing.

Security should be made simple-stupid, because after you've been hacked is not the time to finally understand what your rules _really_ meant.
Comment 4 Jacob Champion 2017-03-31 18:15:36 UTC
Yeah, this is a case where the design seems conceptually sound but the end user experience is not.

I am agreed that we should not, from an architectural perspective, allow access to a resource if there are no modules positively indicating that authorization is granted. But from a user perspective, I'd argue that many people intend for

    Require not env disallowed

to *be* a positive declaration: "Allow anyone who isn't explicitly disallowed." We should let users express this in a way that doesn't require five lines of boolean logic.

(Well, I suppose we do, but it's not nearly as easy to parse:

    Require expr "-z %{reqenv:disallowed}"

It's not intuitive that this check, which is effectively checking for the absence of something, is considered positive authorization, but `Require not` isn't.)

<idle thoughts>
Does part of the confusion stem from the fact that we are <RequireAny> by default instead of <RequireAll>? Switching that alone might make some things more intuitive.
</idle thoughts>
Comment 5 Philip Prindeville 2017-03-31 18:31:40 UTC
(In reply to Jacob Champion from comment #4)

> <idle thoughts>
> Does part of the confusion stem from the fact that we are <RequireAny> by
> default instead of <RequireAll>? Switching that alone might make some things
> more intuitive.
> </idle thoughts>

Except that when one things of traditional mandatory access controls like ACLs and such, you execute the rules until you get your first conclusive match...  which is what <RequireAny> does.

So changing over to <RequireAll> would be changing one violation of The Principle of Least Astonishment for another... more of a lateral move than a forward one.
Comment 6 Jacob Champion 2017-03-31 18:58:55 UTC
(In reply to Philip Prindeville from comment #5)
> (In reply to Jacob Champion from comment #4)
> 
> > <idle thoughts>
> > Does part of the confusion stem from the fact that we are <RequireAny> by
> > default instead of <RequireAll>? Switching that alone might make some things
> > more intuitive.
> > </idle thoughts>
> 
> Except that when one things of traditional mandatory access controls like
> ACLs and such, you execute the rules until you get your first conclusive
> match...  which is what <RequireAny> does.

While that might be true -- and I'm not convinced that's an accurate description of all MAC systems -- we're not using an ACL (or a MAC) authorization system here. It's a very flexible (perhaps too flexible), multi-paradigm system, and I would argue that you're just as likely to see role-based authz with some of the more advanced authorization modules.

Perhaps the best thing to agree on is that any behavior might be "astonishing" to some, and we should try to do what is least astonishing to the widest possible range of users.

Anyway: there's a good chance that this is neither here nor there. Maybe all we need to do is review what directives are considered neutral/success/failure in the authz system.
Comment 7 Philip Prindeville 2017-03-31 19:59:35 UTC
(In reply to Jacob Champion from comment #6)

> While that might be true -- and I'm not convinced that's an accurate
> description of all MAC systems -- we're not using an ACL (or a MAC)
> authorization system here. It's a very flexible (perhaps too flexible),
> multi-paradigm system, and I would argue that you're just as likely to see
> role-based authz with some of the more advanced authorization modules.

Yes, sorry.  I was thinking specifically of the case where you're controlling your decision based on IP address, host port, or some derivation of that (like GeoIP).

> Perhaps the best thing to agree on is that any behavior might be
> "astonishing" to some, and we should try to do what is least astonishing to
> the widest possible range of users.

I can subscribe to that.

> Anyway: there's a good chance that this is neither here nor there. Maybe all
> we need to do is review what directives are considered
> neutral/success/failure in the authz system.

Someone explain to me again what the point of "neutral" is?

I've always thought that success == !failure and vice versa.