nginx/src/core/ngx_crypt.c

284 lines
6.7 KiB
C
Raw Normal View History

/*
* Copyright (C) Maxim Dounin
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_crypt.h>
#include <ngx_md5.h>
#if (NGX_HAVE_SHA1)
#include <ngx_sha1.h>
#endif
#if (NGX_CRYPT)
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);
static ngx_int_t ngx_crypt_sha(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);
} else if (ngx_strncmp(salt, "{SHA}", sizeof("{SHA}") - 1) == 0) {
return ngx_crypt_sha(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;
2014-11-28 21:57:23 +08:00
/* Apache's apr1 crypt is Poul-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, (u_char *) "$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 */
Core: fix misallocation at ngx_crypt_apr1 (ticket #412). Found by using auth_basic.t from mdounin nginx-tests under valgrind. ==10470== Invalid write of size 1 ==10470== at 0x43603D: ngx_crypt_to64 (ngx_crypt.c:168) ==10470== by 0x43648E: ngx_crypt (ngx_crypt.c:153) ==10470== by 0x489D8B: ngx_http_auth_basic_crypt_handler (ngx_http_auth_basic_module.c:297) ==10470== by 0x48A24A: ngx_http_auth_basic_handler (ngx_http_auth_basic_module.c:240) ==10470== by 0x44EAB9: ngx_http_core_access_phase (ngx_http_core_module.c:1121) ==10470== by 0x44A822: ngx_http_core_run_phases (ngx_http_core_module.c:895) ==10470== by 0x44A932: ngx_http_handler (ngx_http_core_module.c:878) ==10470== by 0x455EEF: ngx_http_process_request (ngx_http_request.c:1852) ==10470== by 0x456527: ngx_http_process_request_headers (ngx_http_request.c:1283) ==10470== by 0x456A91: ngx_http_process_request_line (ngx_http_request.c:964) ==10470== by 0x457097: ngx_http_wait_request_handler (ngx_http_request.c:486) ==10470== by 0x4411EE: ngx_epoll_process_events (ngx_epoll_module.c:691) ==10470== Address 0x5866fab is 0 bytes after a block of size 27 alloc'd ==10470== at 0x4A074CD: malloc (vg_replace_malloc.c:236) ==10470== by 0x43B251: ngx_alloc (ngx_alloc.c:22) ==10470== by 0x421B0D: ngx_malloc (ngx_palloc.c:119) ==10470== by 0x421B65: ngx_pnalloc (ngx_palloc.c:147) ==10470== by 0x436368: ngx_crypt (ngx_crypt.c:140) ==10470== by 0x489D8B: ngx_http_auth_basic_crypt_handler (ngx_http_auth_basic_module.c:297) ==10470== by 0x48A24A: ngx_http_auth_basic_handler (ngx_http_auth_basic_module.c:240) ==10470== by 0x44EAB9: ngx_http_core_access_phase (ngx_http_core_module.c:1121) ==10470== by 0x44A822: ngx_http_core_run_phases (ngx_http_core_module.c:895) ==10470== by 0x44A932: ngx_http_handler (ngx_http_core_module.c:878) ==10470== by 0x455EEF: ngx_http_process_request (ngx_http_request.c:1852) ==10470== by 0x456527: ngx_http_process_request_headers (ngx_http_request.c:1283) ==10470==
2013-09-20 22:57:21 +08:00
*encrypted = ngx_pnalloc(pool, sizeof("$apr1$") - 1 + saltlen + 1 + 22 + 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_int_t rc;
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);
len = ngx_max(ngx_base64_decoded_length(encoded.len), 20);
decoded.data = ngx_pnalloc(pool, len);
if (decoded.data == NULL) {
return NGX_ERROR;
}
rc = ngx_decode_base64(&decoded, &encoded);
if (rc != NGX_OK || decoded.len < 20) {
decoded.len = 20;
}
/* 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;
}
static ngx_int_t
ngx_crypt_sha(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;
u_char digest[20];
/* "{SHA}" base64(SHA1(key)) */
decoded.len = sizeof(digest);
decoded.data = digest;
ngx_sha1_init(&sha1);
ngx_sha1_update(&sha1, key, ngx_strlen(key));
ngx_sha1_final(digest, &sha1);
len = sizeof("{SHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;
*encrypted = ngx_pnalloc(pool, len);
if (*encrypted == NULL) {
return NGX_ERROR;
}
encoded.data = ngx_cpymem(*encrypted, "{SHA}", sizeof("{SHA}") - 1);
ngx_encode_base64(&encoded, &decoded);
encoded.data[encoded.len] = '\0';
return NGX_OK;
}
#endif /* NGX_HAVE_SHA1 */
#endif /* NGX_CRYPT */