Add AuthBasicUseDigestAlgorithm directive to allow migration of

passwords from digest to basic authentication.

Proposed by: chrisd
Reviewed by: jim, rjung

Backport of r1514064 from trunk.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1528957 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Rainer Jung
2013-10-03 18:22:48 +00:00
parent 8535a892fe
commit 02c3c0b3bb
4 changed files with 141 additions and 8 deletions

View File

@ -2,6 +2,10 @@
Changes with Apache 2.4.7
*) mod_auth_basic: Add AuthBasicUseDigestAlgorithm directive to
allow migration of passwords from digest to basic authentication.
[Chris Darroch]
*) ab: Add a new -l parameter in order not to check the length of the responses.
This can be usefull with dynamic pages.
PR9945, PR27888, PR42040 [<ccikrs1 cranbrook edu>]

7
STATUS
View File

@ -97,13 +97,6 @@ RELEASE SHOWSTOPPERS:
PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
[ start all new proposals below, under PATCHES PROPOSED. ]
* mod_auth_basic: Add AuthBasicUseDigestAlgorithm directive to allow
migration of passwords from digest to basic authentication.
trunk patch: http://svn.apache.org/viewvc?view=revision&revision=1514064
2.4.x patch: trunk patch works, modulo CHANGES, next-number, and
doc compatibility version note
+1: chrisd, jim, rjung
* core: name-based vhosts printed twice in apachectl -S since
dropping NameVirtualHost directive.
trunk patch: http://svn.apache.org/r1485675 , http://svn.apache.org/r1525000

View File

@ -181,4 +181,80 @@ username and password</description>
</usage>
</directivesynopsis>
<directivesynopsis>
<name>AuthBasicUseDigestAlgorithm</name>
<description>Check passwords against the authentication providers as if
Digest Authentication was in force instead of Basic Authentication.
</description>
<syntax>AuthBasicUseDigestAlgorithm MD5|Off</syntax>
<default>AuthBasicUseDigestAlgorithm Off</default>
<contextlist><context>directory</context><context>.htaccess</context>
</contextlist>
<override>AuthConfig</override>
<compatibility>Apache HTTP Server 2.4.7 and later</compatibility>
<usage>
<p>Normally, when using Basic Authentication, the providers listed in
<directive module="mod_auth_basic">AuthBasicProvider</directive>
attempt to verify a user by checking their data stores for
a matching username and associated password. The stored passwords
are usually encrypted, but not necessarily so; each provider may
choose its own storage scheme for passwords.</p>
<p>When using <directive
module="mod_auth_digest">AuthDigestProvider</directive> and Digest
Authentication, providers perform a similar check to find a matching
username in their data stores. However, unlike in the Basic
Authentication case, the value associated with each stored username
must be an encrypted string composed from the username, realm name,
and password. (See
<a href="http://tools.ietf.org/html/rfc2617#section-3.2.2.2">
RFC 2617, Section 3.2.2.2</a> for more details on the format used
for this encrypted string.)</p>
<p>As a consequence of the difference in the stored values between
Basic and Digest Authentication, converting from Digest
Authentication to Basic Authentication generally requires that all
users be assigned new passwords, as their existing passwords cannot
be recovered from the password storage scheme imposed on those
providers which support Digest Authentication.</p>
<p>Setting the <directive
module="mod_auth_basic">AuthBasicUseDigestAlgorithm</directive> directive
to <code>MD5</code> will cause the user's Basic Authentication password
to be checked using the same encrypted format as for Digest
Authentication. First a string composed from the username, realm name,
and password is hashed with MD5; then the username and this encrypted
string are passed to the providers listed in
<directive module="mod_auth_basic">AuthBasicProvider</directive>
as if
<directive module="mod_authn_core">AuthType</directive>
was set to <code>Digest</code> and Digest Authentication was in force.
</p>
<p>Through the use of <directive
module="mod_auth_basic">AuthBasicUseDigestAlgorithm</directive>
a site may switch from Digest to Basic Authentication without
requiring users to be assigned new passwords.</p>
<note>
The inverse process of switching from Basic to Digest
Authentication without assigning new passwords is generally
not possible. Only if the Basic Authentication passwords
have been stored in plain text or with a reversable encryption
scheme will it be possible to recover them and generate a
new data store following the Digest Authentication password
storage scheme.
</note>
<note>
Only providers which support Digest Authentication will be able
to authenticate users when <directive
module="mod_auth_basic">AuthBasicUseDigestAlgorithm</directive>
is set to <code>MD5</code>. Use of other providers will result
in an error response and the client will be denied access.
</note>
</usage>
</directivesynopsis>
</modulesynopsis>

View File

@ -27,6 +27,7 @@
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
#include "util_md5.h"
#include "ap_provider.h"
#include "ap_expr.h"
@ -38,7 +39,9 @@ typedef struct {
int authoritative;
ap_expr_info_t *fakeuser;
ap_expr_info_t *fakepass;
const char *use_digest_algorithm;
int fake_set:1;
int use_digest_algorithm_set:1;
int authoritative_set:1;
} auth_basic_config_rec;
@ -70,6 +73,12 @@ static void *merge_auth_basic_dir_config(apr_pool_t *p, void *basev, void *overr
overrides->fake_set ? overrides->fakepass : base->fakepass;
newconf->fake_set = overrides->fake_set || base->fake_set;
newconf->use_digest_algorithm =
overrides->use_digest_algorithm_set ? overrides->use_digest_algorithm
: base->use_digest_algorithm;
newconf->use_digest_algorithm_set =
overrides->use_digest_algorithm_set || base->use_digest_algorithm_set;
newconf->providers = overrides->providers ? overrides->providers : base->providers;
return newconf;
@ -175,6 +184,23 @@ static const char *add_basic_fake(cmd_parms * cmd, void *config,
return NULL;
}
static const char *set_use_digest_algorithm(cmd_parms *cmd, void *config,
const char *alg)
{
auth_basic_config_rec *conf = (auth_basic_config_rec *)config;
if (strcasecmp(alg, "Off") && strcasecmp(alg, "MD5")) {
return apr_pstrcat(cmd->pool,
"Invalid algorithm in "
"AuthBasicUseDigestAlgorithm: ", alg, NULL);
}
conf->use_digest_algorithm = apr_pstrdup(cmd->pool, alg);
conf->use_digest_algorithm_set = 1;
return NULL;
}
static const command_rec auth_basic_cmds[] =
{
AP_INIT_ITERATE("AuthBasicProvider", add_authn_provider, NULL, OR_AUTHCFG,
@ -186,6 +212,10 @@ static const command_rec auth_basic_cmds[] =
"Fake basic authentication using the given expressions for "
"username and password, 'off' to disable. Password defaults "
"to 'password' if missing."),
AP_INIT_TAKE1("AuthBasicUseDigestAlgorithm", set_use_digest_algorithm,
NULL, OR_AUTHCFG,
"Set to 'MD5' to use the auth provider's authentication "
"check for digest auth, using a hash of 'user:realm:pass'"),
{NULL}
};
@ -271,6 +301,8 @@ static int authenticate_basic_user(request_rec *r)
auth_basic_config_rec *conf = ap_get_module_config(r->per_dir_config,
&auth_basic_module);
const char *sent_user, *sent_pw, *current_auth;
const char *realm = NULL;
const char *digest = NULL;
int res;
authn_status auth_result;
authn_provider_list *current_provider;
@ -295,6 +327,15 @@ static int authenticate_basic_user(request_rec *r)
return res;
}
if (conf->use_digest_algorithm
&& !strcasecmp(conf->use_digest_algorithm, "MD5")) {
realm = ap_auth_name(r);
digest = ap_md5(r->pool,
(unsigned char *)apr_pstrcat(r->pool, sent_user, ":",
realm, ":",
sent_pw, NULL));
}
current_provider = conf->providers;
do {
const authn_provider *provider;
@ -320,8 +361,27 @@ static int authenticate_basic_user(request_rec *r)
apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, current_provider->provider_name);
}
if (digest) {
char *password;
auth_result = provider->check_password(r, sent_user, sent_pw);
if (!provider->get_realm_hash) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02493)
"Authn provider does not support "
"AuthBasicUseDigestAlgorithm");
auth_result = AUTH_GENERAL_ERROR;
break;
}
/* We expect the password to be hash of user:realm:password */
auth_result = provider->get_realm_hash(r, sent_user, realm,
&password);
if (auth_result == AUTH_USER_FOUND) {
auth_result = strcmp(digest, password) ? AUTH_DENIED
: AUTH_GRANTED;
}
}
else {
auth_result = provider->check_password(r, sent_user, sent_pw);
}
apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE);