"$apr1", "{PLAIN}", and "{SSHA}" password methods in auth basic module
patch by Maxim Dounin
diff --git a/src/core/ngx_crypt.c b/src/core/ngx_crypt.c
new file mode 100644
index 0000000..2538cfc
--- /dev/null
+++ b/src/core/ngx_crypt.c
@@ -0,0 +1,234 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_md5.h>
+#if (NGX_HAVE_SHA1)
+#include <ngx_sha1.h>
+#endif
+
+
+static ngx_int_t ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+static ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+
+#if (NGX_HAVE_SHA1)
+
+static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+
+#endif
+
+
+static u_char *ngx_crypt_to64(u_char *p, uint32_t v, size_t n);
+
+
+ngx_int_t
+ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ if (ngx_strncmp(salt, "$apr1$", sizeof("$apr1$") - 1) == 0) {
+ return ngx_crypt_apr1(pool, key, salt, encrypted);
+
+ } else if (ngx_strncmp(salt, "{PLAIN}", sizeof("{PLAIN}") - 1) == 0) {
+ return ngx_crypt_plain(pool, key, salt, encrypted);
+
+#if (NGX_HAVE_SHA1)
+ } else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) {
+ return ngx_crypt_ssha(pool, key, salt, encrypted);
+#endif
+ }
+
+ /* fallback to libc crypt() */
+
+ return ngx_libc_crypt(pool, key, salt, encrypted);
+}
+
+
+static ngx_int_t
+ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ ngx_int_t n;
+ ngx_uint_t i;
+ u_char *p, *last, final[16];
+ size_t saltlen, keylen;
+ ngx_md5_t md5, ctx1;
+
+ /* Apache's apr1 crypt is Paul-Henning Kamp's md5 crypt with $apr1$ magic */
+
+ keylen = ngx_strlen(key);
+
+ /* true salt: no magic, max 8 chars, stop at first $ */
+
+ salt += sizeof("$apr1$") - 1;
+ last = salt + 8;
+ for (p = salt; *p && *p != '$' && p < last; p++) { /* void */ }
+ saltlen = p - salt;
+
+ /* hash key and salt */
+
+ ngx_md5_init(&md5);
+ ngx_md5_update(&md5, key, keylen);
+ ngx_md5_update(&md5, "$apr1$", sizeof("$apr1$") - 1);
+ ngx_md5_update(&md5, salt, saltlen);
+
+ ngx_md5_init(&ctx1);
+ ngx_md5_update(&ctx1, key, keylen);
+ ngx_md5_update(&ctx1, salt, saltlen);
+ ngx_md5_update(&ctx1, key, keylen);
+ ngx_md5_final(final, &ctx1);
+
+ for (n = keylen; n > 0; n -= 16) {
+ ngx_md5_update(&md5, final, n > 16 ? 16 : n);
+ }
+
+ ngx_memzero(final, sizeof(final));
+
+ for (i = keylen; i; i >>= 1) {
+ if (i & 1) {
+ ngx_md5_update(&md5, final, 1);
+
+ } else {
+ ngx_md5_update(&md5, key, 1);
+ }
+ }
+
+ ngx_md5_final(final, &md5);
+
+ for (i = 0; i < 1000; i++) {
+ ngx_md5_init(&ctx1);
+
+ if (i & 1) {
+ ngx_md5_update(&ctx1, key, keylen);
+
+ } else {
+ ngx_md5_update(&ctx1, final, 16);
+ }
+
+ if (i % 3) {
+ ngx_md5_update(&ctx1, salt, saltlen);
+ }
+
+ if (i % 7) {
+ ngx_md5_update(&ctx1, key, keylen);
+ }
+
+ if (i & 1) {
+ ngx_md5_update(&ctx1, final, 16);
+
+ } else {
+ ngx_md5_update(&ctx1, key, keylen);
+ }
+
+ ngx_md5_final(final, &ctx1);
+ }
+
+ /* output */
+
+ *encrypted = ngx_pnalloc(pool, sizeof("$apr1$") - 1 + saltlen + 16 + 1);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(*encrypted, "$apr1$", sizeof("$apr1$") - 1);
+ p = ngx_copy(p, salt, saltlen);
+ *p++ = '$';
+
+ p = ngx_crypt_to64(p, (final[ 0]<<16) | (final[ 6]<<8) | final[12], 4);
+ p = ngx_crypt_to64(p, (final[ 1]<<16) | (final[ 7]<<8) | final[13], 4);
+ p = ngx_crypt_to64(p, (final[ 2]<<16) | (final[ 8]<<8) | final[14], 4);
+ p = ngx_crypt_to64(p, (final[ 3]<<16) | (final[ 9]<<8) | final[15], 4);
+ p = ngx_crypt_to64(p, (final[ 4]<<16) | (final[10]<<8) | final[ 5], 4);
+ p = ngx_crypt_to64(p, final[11], 2);
+ *p = '\0';
+
+ return NGX_OK;
+}
+
+
+static u_char *
+ngx_crypt_to64(u_char *p, uint32_t v, size_t n)
+{
+ static u_char itoa64[] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ while (n--) {
+ *p++ = itoa64[v & 0x3f];
+ v >>= 6;
+ }
+
+ return p;
+}
+
+
+static ngx_int_t
+ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ size_t len;
+ u_char *p;
+
+ len = ngx_strlen(key);
+
+ *encrypted = ngx_pnalloc(pool, sizeof("{PLAIN}") - 1 + len + 1);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(*encrypted, "{PLAIN}", sizeof("{PLAIN}") - 1);
+ ngx_memcpy(p, key, len + 1);
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_SHA1)
+
+static ngx_int_t
+ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ size_t len;
+ ngx_str_t encoded, decoded;
+ ngx_sha1_t sha1;
+
+ /* "{SSHA}" base64(SHA1(key salt) salt) */
+
+ /* decode base64 salt to find out true salt */
+
+ encoded.data = salt + sizeof("{SSHA}") - 1;
+ encoded.len = ngx_strlen(encoded.data);
+
+ decoded.data = ngx_pnalloc(pool, ngx_base64_decoded_length(encoded.len));
+ if (decoded.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_decode_base64(&decoded, &encoded);
+
+ /* update SHA1 from key and salt */
+
+ ngx_sha1_init(&sha1);
+ ngx_sha1_update(&sha1, key, ngx_strlen(key));
+ ngx_sha1_update(&sha1, decoded.data + 20, decoded.len - 20);
+ ngx_sha1_final(decoded.data, &sha1);
+
+ /* encode it back to base64 */
+
+ len = sizeof("{SSHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;
+
+ *encrypted = ngx_pnalloc(pool, len);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ encoded.data = ngx_cpymem(*encrypted, "{SSHA}", sizeof("{SSHA}") - 1);
+ ngx_encode_base64(&encoded, &decoded);
+ encoded.data[encoded.len] = '\0';
+
+ return NGX_OK;
+}
+
+#endif /* NGX_HAVE_SHA1 */
diff --git a/src/core/ngx_crypt.h b/src/core/ngx_crypt.h
new file mode 100644
index 0000000..45ef81d
--- /dev/null
+++ b/src/core/ngx_crypt.h
@@ -0,0 +1,19 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CRYPT_H_INCLUDED_
+#define _NGX_CRYPT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+
+
+#endif /* _NGX_CRYPT_H_INCLUDED_ */
diff --git a/src/http/modules/ngx_http_auth_basic_module.c b/src/http/modules/ngx_http_auth_basic_module.c
index c6b7f92..45168de 100644
--- a/src/http/modules/ngx_http_auth_basic_module.c
+++ b/src/http/modules/ngx_http_auth_basic_module.c
@@ -7,6 +7,7 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
+#include <ngx_crypt.h>
#define NGX_HTTP_AUTH_BUF_SIZE 2048
diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c
index 165c6a4..c46fb2b 100644
--- a/src/os/unix/ngx_user.c
+++ b/src/os/unix/ngx_user.c
@@ -23,7 +23,7 @@
#if (NGX_HAVE_GNU_CRYPT_R)
ngx_int_t
-ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
{
char *value;
size_t len;
@@ -58,7 +58,7 @@
#else
ngx_int_t
-ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
{
char *value;
size_t len;
diff --git a/src/os/unix/ngx_user.h b/src/os/unix/ngx_user.h
index 7cd944d..68d1c6e 100644
--- a/src/os/unix/ngx_user.h
+++ b/src/os/unix/ngx_user.h
@@ -16,7 +16,7 @@
typedef gid_t ngx_gid_t;
-ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
+ngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
u_char **encrypted);
diff --git a/src/os/win32/ngx_user.c b/src/os/win32/ngx_user.c
index cc07603..5167870 100644
--- a/src/os/win32/ngx_user.c
+++ b/src/os/win32/ngx_user.c
@@ -10,7 +10,7 @@
#if (NGX_CRYPT)
ngx_int_t
-ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
{
/* STUB: a plain text password */
diff --git a/src/os/win32/ngx_user.h b/src/os/win32/ngx_user.h
index 6e8c58b..fd30df2 100644
--- a/src/os/win32/ngx_user.h
+++ b/src/os/win32/ngx_user.h
@@ -17,7 +17,7 @@
#define ngx_gid_t ngx_int_t
-ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
+ngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
u_char **encrypted);