# HG changeset patch # User Igor Sysoev # Date 1305557690 0 # Node ID 9c057d5e1c27148c14e3d92abba0fa9ac4d4f60c # Parent bab3488bd113344308fa94401c135f84c71c7e3e "$apr1", "{PLAIN}", and "{SSHA}" password methods in auth basic module patch by Maxim Dounin diff --git a/auto/lib/conf b/auto/lib/conf --- a/auto/lib/conf +++ b/auto/lib/conf @@ -43,6 +43,7 @@ if [ $USE_SHA1 = YES ]; then if [ $USE_OPENSSL = YES ]; then have=NGX_HAVE_OPENSSL_SHA1_H . auto/have + have=NGX_HAVE_SHA1 . auto/have SHA1=YES SHA1_LIB=OpenSSL diff --git a/auto/lib/sha1/conf b/auto/lib/sha1/conf --- a/auto/lib/sha1/conf +++ b/auto/lib/sha1/conf @@ -4,6 +4,7 @@ if [ $SHA1 != NONE ]; then + have=NGX_HAVE_SHA1 . auto/have CORE_INCS="$CORE_INCS $SHA1" case "$NGX_CC_NAME" in @@ -41,7 +42,7 @@ else # FreeBSD ngx_feature="sha1 in system md library" - ngx_feature_name= + ngx_feature_name=NGX_HAVE_SHA1 ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path= diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -197,6 +197,8 @@ if [ $HTTP_RANDOM_INDEX = YES ]; then fi if [ $HTTP_AUTH_BASIC = YES ]; then + USE_MD5=YES + USE_SHA1=YES have=NGX_CRYPT . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_AUTH_BASIC_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_AUTH_BASIC_SRCS" diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -34,7 +34,8 @@ CORE_DEPS="src/core/nginx.h \ src/core/ngx_cycle.h \ src/core/ngx_conf_file.h \ src/core/ngx_resolver.h \ - src/core/ngx_open_file_cache.h" + src/core/ngx_open_file_cache.h \ + src/core/ngx_crypt.h" CORE_SRCS="src/core/nginx.c \ @@ -64,7 +65,8 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_cpuinfo.c \ src/core/ngx_conf_file.c \ src/core/ngx_resolver.c \ - src/core/ngx_open_file_cache.c" + src/core/ngx_open_file_cache.c \ + src/core/ngx_crypt.c" REGEX_DEPS=src/core/ngx_regex.h diff --git a/src/core/ngx_crypt.c b/src/core/ngx_crypt.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_crypt.c @@ -0,0 +1,234 @@ + +/* + * Copyright (C) Maxim Dounin + */ + + +#include +#include +#include +#if (NGX_HAVE_SHA1) +#include +#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 --- /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 +#include + + +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 --- 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 #include #include +#include #define NGX_HTTP_AUTH_BUF_SIZE 2048 diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c --- 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 @@ ngx_crypt(ngx_pool_t *pool, u_char *key, #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 --- a/src/os/unix/ngx_user.h +++ b/src/os/unix/ngx_user.h @@ -16,7 +16,7 @@ typedef uid_t ngx_uid_t; 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 --- 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 --- 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);