view src/core/ngx_crypt.c @ 4436:311b358658c8 stable-1.0

Merge of r4339, r4340, r4341: Cache fixes: *) Obsolete code removed. The ngx_http_cache() and ngx_http_no_cache_set_slot() functions were replaced in 0.8.46 and no longer used since then. *) Handling of cache files with long headers. There are two possible situations which can lead to this: response was cached with bigger proxy_buffer_size value (and nginx was restared since then, i.e. shared memory zone content was lost), or due to the race in the cache update code (see [1]) we've end up with fcn->body_start from a different response stored in shared memory zone. *) Only complain on long locked entries. There have been multiple reports of cases where a real locked entry was removed, resulting in a segmentation fault later in a worker which locked the entry. It looks like default inactive timeout isn't enough in real life. For now just ignore such locked entries, and move them to the top of the inactive queue to allow processing of other entries. [1] http://mailman.nginx.org/pipermail/nginx-devel/2011-September/001287.html
author Maxim Dounin <mdounin@mdounin.ru>
date Sun, 05 Feb 2012 13:34:08 +0000
parents 105841a157b9
children 4c36e15651f7
line wrap: on
line source


/*
 * 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


#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);

#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, (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 */

    *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 */

#endif /* NGX_CRYPT */