From 02c3c0b3bbb4fe63b035e82ab871f4af26cccedf Mon Sep 17 00:00:00 2001 From: Rainer Jung Date: Thu, 3 Oct 2013 18:22:48 +0000 Subject: [PATCH] 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 --- CHANGES | 4 ++ STATUS | 7 --- docs/manual/mod/mod_auth_basic.xml | 76 ++++++++++++++++++++++++++++++ modules/aaa/mod_auth_basic.c | 62 +++++++++++++++++++++++- 4 files changed, 141 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 05745a1d4c..93f025200e 100644 --- a/CHANGES +++ b/CHANGES @@ -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 [] diff --git a/STATUS b/STATUS index 3f8ce61d5a..ba0aae346a 100644 --- a/STATUS +++ b/STATUS @@ -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 diff --git a/docs/manual/mod/mod_auth_basic.xml b/docs/manual/mod/mod_auth_basic.xml index c8cda57b8f..145c504778 100644 --- a/docs/manual/mod/mod_auth_basic.xml +++ b/docs/manual/mod/mod_auth_basic.xml @@ -181,4 +181,80 @@ username and password + +AuthBasicUseDigestAlgorithm +Check passwords against the authentication providers as if +Digest Authentication was in force instead of Basic Authentication. + +AuthBasicUseDigestAlgorithm MD5|Off +AuthBasicUseDigestAlgorithm Off +directory.htaccess + +AuthConfig +Apache HTTP Server 2.4.7 and later + + +

Normally, when using Basic Authentication, the providers listed in + AuthBasicProvider + 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.

+ +

When using AuthDigestProvider 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 + + RFC 2617, Section 3.2.2.2 for more details on the format used + for this encrypted string.)

+ +

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.

+ +

Setting the AuthBasicUseDigestAlgorithm directive + to MD5 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 + AuthBasicProvider + as if + AuthType + was set to Digest and Digest Authentication was in force. +

+ +

Through the use of AuthBasicUseDigestAlgorithm + a site may switch from Digest to Basic Authentication without + requiring users to be assigned new passwords.

+ + + 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. + + + + Only providers which support Digest Authentication will be able + to authenticate users when AuthBasicUseDigestAlgorithm + is set to MD5. Use of other providers will result + in an error response and the client will be denied access. + +
+
+ diff --git a/modules/aaa/mod_auth_basic.c b/modules/aaa/mod_auth_basic.c index 8c1367b30a..75044d4831 100644 --- a/modules/aaa/mod_auth_basic.c +++ b/modules/aaa/mod_auth_basic.c @@ -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); -- 2.40.0