summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--src/http_auth.c38
-rw-r--r--tests/lighttpd.htpasswd1
-rwxr-xr-xtests/mod-auth.t20
4 files changed, 59 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index bbbf398e..58b802fa 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,7 @@ NEWS
* call ERR_clear_error only for ssl connections in CON_STATE_ERROR
* reject non ASCII characters in HTTP header names
* [mod_auth] use crypt() on encrypted password instead of extracting salt first (fixes #2483)
+ * [mod_auth] add htpasswd -s (SHA1) support if openssl is used (needs openssl for SHA1). This doesn't use any salt, md5 with salt is probably better.
- 1.4.32 - 2012-11-21
* Code cleanup with clang/sparse (fixes #2437, thx kibi)
diff --git a/src/http_auth.c b/src/http_auth.c
index 451d5d70..c6c8a118 100644
--- a/src/http_auth.c
+++ b/src/http_auth.c
@@ -29,6 +29,10 @@
#include "md5.h"
+#ifdef USE_OPENSSL
+#include <openssl/sha.h>
+#endif
+
#define HASHLEN 16
#define HASHHEXLEN 32
typedef unsigned char HASH[HASHLEN];
@@ -599,6 +603,35 @@ static void apr_md5_encode(const char *pw, const char *salt, char *result, size_
apr_cpystrn(result, passwd, nbytes - 1);
}
+#ifdef USE_OPENSSL
+static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {
+ static const unsigned char base64_data[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ unsigned char digest[21]; /* multiple of 3 for base64 encoding */
+ int i;
+
+ memset(result, 0, nbytes);
+
+ /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */
+ if (nbytes < 5 + 28 + 1) return;
+
+ SHA1((const unsigned char*) pw, strlen(pw), digest);
+ digest[20] = 0;
+
+ strcpy(result, "{SHA}");
+ result = result + 5;
+ for (i = 0; i < 21; i += 3) {
+ unsigned int v = (digest[i] << 16) | (digest[i+1] << 8) | digest[i+2];
+ result[3] = base64_data[v & 0x3f]; v >>= 6;
+ result[2] = base64_data[v & 0x3f]; v >>= 6;
+ result[1] = base64_data[v & 0x3f]; v >>= 6;
+ result[0] = base64_data[v & 0x3f];
+ result += 4;
+ }
+ result[-1] = '='; /* last digest character was already end of string, pad it */
+ *result = '\0';
+}
+#endif
/**
*
@@ -643,6 +676,11 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p
*/
apr_md5_encode(pw, password->ptr, sample, sizeof(sample));
return (strcmp(sample, password->ptr) == 0) ? 0 : 1;
+#ifdef USE_OPENSSL
+ } else if (0 == strncmp(password->ptr, "{SHA}", 5)) {
+ apr_sha_encode(pw, sample, sizeof(sample));
+ return (strcmp(sample, password->ptr) == 0) ? 0 : 1;
+#endif
} else {
#ifdef HAVE_CRYPT
char *crypted;
diff --git a/tests/lighttpd.htpasswd b/tests/lighttpd.htpasswd
index 1faf25a4..eed7dd26 100644
--- a/tests/lighttpd.htpasswd
+++ b/tests/lighttpd.htpasswd
@@ -1,2 +1,3 @@
des:12tMnfw882VDQ
md5:$1$md5$kIa7Juuiv8zja0ILQPR36/
+sha:{SHA}2PRZAyDhNDqRW2OUFwZQqPNdaSY=
diff --git a/tests/mod-auth.t b/tests/mod-auth.t
index 89ead9dd..e070df10 100755
--- a/tests/mod-auth.t
+++ b/tests/mod-auth.t
@@ -8,7 +8,7 @@ BEGIN {
use strict;
use IO::Socket;
-use Test::More tests => 15;
+use Test::More tests => 17;
use LightyTest;
my $tf = LightyTest->new();
@@ -65,6 +65,24 @@ EOF
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (des) (lowercase)');
+$t->{REQUEST} = ( <<EOF
+GET /server-config HTTP/1.0
+Host: auth-htpasswd.example.org
+Authorization: Basic c2hhOnNoYQ==
+EOF
+ );
+$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
+ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (sha)');
+
+$t->{REQUEST} = ( <<EOF
+GET /server-config HTTP/1.0
+Host: auth-htpasswd.example.org
+Authorization: Basic c2hhOnNoYg==
+EOF
+ );
+$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
+ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (sha, wrong password)');
+
SKIP: {
skip "no md5 for crypt under cygwin", 1 if $^O eq 'cygwin';