CVE-2015-3185 for httpd and CVE-2015-3184 for Subversion combined to fix
a Subversion authz vulnerability. Following the release (httpd 2.4.16
and Subversion 1.8.14) there have been reports of authn failures with
mod_auth_kerb and mod_auth_ntlm. These are 3rd party modules
maintained outside the httpd tree.
https://bugs.debian.org/797216
https://bugs.debian.org/799105
http://mail-archives.apache.org/mod_mbox/subversion-users/201509.mbox/%3CF3CBF2565B5E6046BC0D960DFE30DDCE78B01E62%40AL-MAIL01.aliconahq.com%3E
http://svn.haxx.se/users/archive-2015-09/0063.shtml
I believe I have reproduced the mod_auth_kerb problem:
$ curl -D- http://localhost:8888/krb/repo/A/B/
HTTP/1.1 401 Unauthorized
Date: Fri, 09 Oct 2015 17:32:46 GMT
Server: Apache/2.4.17-dev (Unix) OpenSSL/1.0.1k SVN/1.10.0-dev mod_auth_kerb/5.4
Content-Length: 381
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
...
The 401 is expected here, but the server is sending it without a
WWW-Authenticate header. A client that could authenticate, like
Subversion, cannot do it because the header is missing and so the client
gives up.
The cause is this code in mod_authz_svn.c:
if (authn_configured) {
/* We have to check to see if authn is required because if so we must
* return UNAUTHORIZED (401) rather than FORBIDDEN (403) since returning
* the 403 leaks information about what paths may exist to
* unauthenticated users. We must set a note here in order
* to use ap_some_authn_rquired() without triggering an infinite
* loop since the call will trigger this function to be called again. */
apr_table_setn(r->notes, IN_SOME_AUTHN_NOTE, (const char*)1);
authn_required = ap_some_authn_required(r);
apr_table_unset(r->notes, IN_SOME_AUTHN_NOTE);
if (authn_required)
{
ap_note_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
}
Returning HTTP_UNAUTHORIZED is part of the CVE fix, mod_authz_svn didn't
do this before. The call to ap_note_auth_failure() runs hooks in the
authn modules to add the authenticate header and works with modules like
mod_auth_basic and mod_auth_digest which use ap_hook_note_auth_failure()
to register the hook. However, note_auth_failure is new in 2.4 and the
3rd party mod_auth_kerb or mod_auth_ntlm do not register. That explains
why they don't add the header and why authn fails.
I don't know whether ap_note_auth_failure is optional or manatory for
authn modules in 2.4 so I don't know if the fault lies with mod_dav_svn
or mod_auth_kerb/mod_auth_ntlm. I do know that httpd's mod_authz_core
uses ap_note_auth_failure().
We could attempt to add the hook to mod_auth_kerb, but we would need to
do the same for mod_auth_ntlm (of which there appears to be several
variations) as well as any other module that doesn't implement the hook.
Before the CVE fix mod_authz_svn would return DECLINED rather than
HTTP_UNAUTHORIZED and this allows httpd's ap_process_request_internal()
to procede further and invoke mod_auth_kerb via an api it does implement
and that adds the authenticate header. I've been experimenting with the
patch below that makes mod_authz_svn return DECLINED again. It passes
the Subversion regression tests, including the ones added for the CVE.
All this authn/authz stuff is very complex and I don't know if this is
correct.
Index: subversion/mod_authz_svn/mod_authz_svn.c
===================================================================
--- subversion/mod_authz_svn/mod_authz_svn.c (revision 1707771)
+++ subversion/mod_authz_svn/mod_authz_svn.c (working copy)
@@ -954,7 +954,7 @@ access_checker(request_rec *r)
#if USE_FORCE_AUTHN
if (authn_configured) {
/* We have to check to see if authn is required because if so we must
- * return UNAUTHORIZED (401) rather than FORBIDDEN (403) since returning
+ * return DECLINED rather than FORBIDDEN (403) since returning
* the 403 leaks information about what paths may exist to
* unauthenticated users. We must set a note here in order
* to use ap_some_authn_rquired() without triggering an infinite
@@ -963,10 +963,7 @@ access_checker(request_rec *r)
authn_required = ap_some_authn_required(r);
apr_table_unset(r->notes, IN_SOME_AUTHN_NOTE);
if (authn_required)
- {
- ap_note_auth_failure(r);
- return HTTP_UNAUTHORIZED;
- }
+ return DECLINED;
}
#else
if (!authn_required)
--
Philip Martin
WANdisco
Received on 2015-10-09 20:20:43 CEST