Redirect based on php-set cookies

tqvn2004 nginx-forum at nginx.us
Thu Feb 10 15:11:35 MSK 2011


[b]For someone who is interested in using this module[/b]

To compile with nginx, do the following steps:

1. Download Nginx source code from http://wiki.nginx.org/NginxInstall,
and put them in, let's say, folder 'nginx'

2. Install OpenSSL for your Linux.

3. You need also to download OpenSSL source code, and copy folder
openssl/crypto/sha to nginx/src. This step is important, as the
secret_cookie module depends on OpenSSL at compile time.

4. Copy sha/sha.h in openssl/crypto/sha into nginx/src/core. You will
get compilation error if not doing this.

5. Now create a folder name nginx/src/secret_cookie to store the
secret_cookie module. Put the config and ngx_http_secret_cookie_module.c
(described below) in this folder.

7. Go to folder nginx and now you can configure the make:

./configure --prefix=/usr/local/nginx-proxy/ --with-http_ssl_module
--add-module=src/secret_cookie --with-sha1=src/sha

Please change the directory as per your server configuration.

8. Run make, if everything is ok, you should now be able to compile
nginx with the secret_cookie module. 

Content of the config file:

[code]ngx_addon_name=ngx_http_secret_cookie_module
HTTP_MODULES="$HTTP_MODULES ngx_http_secret_cookie_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS
$ngx_addon_dir/ngx_http_secret_cookie_module.c" 
[/code]

Content of the ngx_http_secret_cookie_module.c file:
[code]
/*
 * Copyright (C) Huan Cong Nguyen
 * Version 1.0.6
 */

#include 
#include 
#include 
#include 

/*
 * This module set the  $secret_cookie_value variable 
 * to the names of valid secret cookies set at client.
 * Based on the variable, user can be classified
 * into verified and non-verified one.
 */
#define NGX_HTTP_SECRET_COOKIE_OFF                  0 // Default state
#define NGX_HTTP_SECRET_COOKIE_ON                   1

#define NGX_HTTP_SECRET_COOKIE_NOT_SET              0

#define NGX_HTTP_SECRET_COOKIE_MINIMUM_DURATION     600    // 10
minutes
#define NGX_HTTP_SECRET_COOKIE_MINIMUM_UA_LIMIT     10     // 10
characters
#define NGX_HTTP_SECRET_COOKIE_MAX_SHORT_STR_SIZE   12     // Allow only
12 characters in name, salt and rule

// Rule elements
#define NGX_HTTP_SECRET_COOKIE_RULE_SALT            's'
#define NGX_HTTP_SECRET_COOKIE_RULE_ADDRESS         'a'
#define NGX_HTTP_SECRET_COOKIE_RULE_TIME            't'
#define NGX_HTTP_SECRET_COOKIE_RULE_USER_AGENT      'u'
#define NGX_HTTP_SECRET_COOKIE_DIVIDER              ' '

// Record for each secret cookie
typedef struct {
    ngx_flag_t                  status;
    ngx_flag_t                  log;
    ngx_uint_t                  duration;
    ngx_str_t                   name;
    ngx_str_t                   rule;
    ngx_str_t                   salt;
    ngx_uint_t                  ua_limit;
} ngx_http_secret_cookie_t;

// Record for Location configuration
typedef struct {
    ngx_flag_t                  status;
    ngx_array_t                 *secret_cookies;
    ngx_http_variable_value_t   *secret_cookie_value_disabled;
} ngx_http_secret_cookie_conf_t;

// Context variable: This alive for request's duration
typedef struct {
    ngx_http_variable_value_t   *secret_cookie_value;
} ngx_http_secret_cookie_ctx_t;

// Return variable structure
typedef struct {
    ngx_str_t                   name;
    ngx_http_get_variable_pt    handler;
    uintptr_t                   data;
} ngx_http_secret_cookie_variable_t;

static void *ngx_http_secret_cookie_create_conf(ngx_conf_t *cf);
static char *ngx_http_secret_cookie_merge_conf(ngx_conf_t *cf, void
*parent,
    void *child);
static ngx_int_t ngx_http_secret_cookie_add_variable(ngx_conf_t *cf);
static ngx_int_t ngx_http_secret_cookie_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data);
static ngx_http_secret_cookie_ctx_t *
ngx_http_secret_cookie_handler(ngx_http_request_t *r,
    ngx_http_secret_cookie_conf_t *cf);
static char *ngx_http_secret_cookie_def(ngx_conf_t *cf, ngx_command_t
*cmd, void *conf);
static u_char * ngx_num2str(u_char *buf, u_char *last, uint64_t ui64);
static ngx_int_t ngx_raw_vs_hex(u_char *rawbuf, size_t rlen, u_char
*hexbuf, size_t hlen);

static ngx_command_t  ngx_http_secret_cookie_commands[] = {
    /* Define secret_cookie directive (on or off). 
     * Location: Main, HTTP
     * Take: 1 parameter, a Flag
     */
    { ngx_string("secret_cookie"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_flag_slot, // Convert on/off to value
      NGX_HTTP_LOC_CONF_OFFSET, // Instruct value to be written to
Location Config,
      offsetof(ngx_http_secret_cookie_conf_t, status), // with this
offset
      NULL }, // No post-handler is necessary.

    /* Define secret_cookie_def directive ([off] name=xyz salt=abc
rule=mnk duration=8323)
     * Location: Main, HTTP
     * Taking: 4 or 5 parameters
     */
    { ngx_string("secret_cookie_def"),
     
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE4|NGX_CONF_TAKE5|NGX_CONF_TAKE6|NGX_CONF_TAKE7,
      ngx_http_secret_cookie_def,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

      ngx_null_command
};


static ngx_http_module_t  ngx_http_secret_cookie_module_ctx = {
    ngx_http_secret_cookie_add_variable,  /* preconfiguration */
    NULL,                                 /* postconfiguration */

    NULL,                                 /* create main configuration
*/
    NULL,                                 /* init main configuration */

    NULL,                                 /* create server configuration
*/
    NULL,                                 /* merge server configuration
*/

    ngx_http_secret_cookie_create_conf,   /* create location
configuration */
    ngx_http_secret_cookie_merge_conf     /* merge location
configuration */
};


ngx_module_t  ngx_http_secret_cookie_module = {
    NGX_MODULE_V1,
    &ngx_http_secret_cookie_module_ctx,    /* module context */
    ngx_http_secret_cookie_commands,       /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

static ngx_http_secret_cookie_variable_t  ngx_http_secret_cookie_vars[]
= {
    { ngx_string("secret_cookie_value"),
ngx_http_secret_cookie_variable,
            NGX_HTTP_SECRET_COOKIE_NOT_SET },
    { ngx_null_string, NULL, 0 }
};

/* 
 * This function set the $secret_cookie_value to found secret_cookie
names,
 * if they are set and valid.
 */ 
static ngx_int_t 
ngx_http_secret_cookie_variable(ngx_http_request_t *r, 
                                ngx_http_variable_value_t *v,
                                uintptr_t data)
{
    ngx_http_secret_cookie_ctx_t   *rctx;
    ngx_http_secret_cookie_conf_t  *cf;

    cf = ngx_http_get_module_loc_conf(r,
ngx_http_secret_cookie_module);
    // Check if the module is enable?
    if (cf->status == NGX_HTTP_SECRET_COOKIE_ON){
        // Only perform cookie search if the module is enable
        rctx = ngx_http_secret_cookie_handler(r, cf);

        if (rctx != NULL){
#if (NGX_DEBUG)
            ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 
                          "secret_cookie: Rctx is returned with
value");
#endif
            *v = *rctx->secret_cookie_value;
            return NGX_OK;
        } else {
            // There is something wrong with the search for secret
cookies, 
            // just return null value to fail all user-defined checks
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 
                          "secret_cookie: Rctx is null, which is often
due to lack of memory");
            *v = ngx_http_variable_null_value;
            return NGX_OK;
        }
    }
    // In case the secret_cookie is disabled, 
    // all defined secret cookies are assumed to be found,
    // so that user-defined checks are successful
    *v = *cf->secret_cookie_value_disabled;
    return NGX_OK;
}

/* 
 * This is the main function: It looks for a secret cookie 
 * in the HTTP request header, and compared with constructed value
 * Return: Secret Cookie SET or NOT SET!
 */
static ngx_http_secret_cookie_ctx_t *
ngx_http_secret_cookie_handler(ngx_http_request_t *r,
ngx_http_secret_cookie_conf_t *cf)
{
    ngx_http_secret_cookie_ctx_t  *ctx;
    ngx_http_secret_cookie_t      *sc;

    ngx_uint_t                    n, i, ua_len;
    ngx_int_t                     m;
    ngx_str_t                     name, rule, salt, last_change,  
                                  remote_address, user_agent,
                                  computed_sc, submitted_sc, sc_string,
                                  secret_cookie_value;
    time_t                        timestamp; // Current UNIX timestamp
    u_char                        *p;
    ngx_sha1_t                    sha_ctx;

    // Try to get the context variable
    ctx = ngx_http_get_module_ctx(r, ngx_http_secret_cookie_module);

    // If context existed, secret_cookie was checked before, so stop!
    if (ctx){
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: Checking was done before,
aborting!");
#endif
        return ctx;
    }
    // Otherwise, start the checking process  
    if (ctx == NULL) {
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: Checking was not done before, start
checking");
#endif
        // This r->pool temporary memory will be destroyed later, 
        // after this request is served!
        ctx = ngx_pcalloc(r->pool,
sizeof(ngx_http_secret_cookie_ctx_t));
        if (ctx == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   "secret_cookie: Out of memory for context variable
(ctx)");
            return NULL;
        }
        ngx_http_set_ctx(r, ctx, ngx_http_secret_cookie_module);
    }
    ctx->secret_cookie_value = ngx_palloc(r->pool,
sizeof(ngx_http_variable_value_t));
    if (ctx->secret_cookie_value == NULL){
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                "secret_cookie: Out of memory for
ctx->secret_cookie_value variable");
        return NULL;
    }
    *ctx->secret_cookie_value = ngx_http_variable_null_value;

    remote_address.len   = r->connection->addr_text.len;
    remote_address.data  = r->connection->addr_text.data;
    if (r->headers_in.user_agent == NULL) {
        // The user-agent value is null
        user_agent.len       = 0;
        user_agent.data      = NULL;
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: User agent is empty");
#endif
    } else {
        // The user-agent value is not null
        user_agent.len       = r->headers_in.user_agent->value.len;
        user_agent.data      = r->headers_in.user_agent->value.data;
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: User agent is not empty");
#endif
    }
    secret_cookie_value.len = 0;
    // Allocate maximum size for this variable
    secret_cookie_value.data = ngx_pcalloc(r->pool,
cf->secret_cookie_value_disabled->len);
    if (secret_cookie_value.data == NULL){
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                     "secret_cookie: Out of memory for
secret_cookie_value string");
        return NULL;
    }

    sc = cf->secret_cookies->elts;
    for (i=0; i < cf->secret_cookies->nelts; i++){
        // Cookie name must be initialized first, in case of jumping to
secret_cookie_found!
        name.len   = sc[i].name.len;
        name.data  = sc[i].name.data;

        if (sc[i].status == NGX_HTTP_SECRET_COOKIE_OFF){
#if (NGX_DEBUG)
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: checking of cookie \"%V\" is off",
&name);
#endif
            goto secret_cookie_found;
        }

        // First, search for the secret_cookie's name in http header:
        m = ngx_http_parse_multi_header_lines(&r->headers_in.cookies,
&name,
                                          &submitted_sc);
        if (m == NGX_DECLINED) {
#if (NGX_DEBUG)
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: cookie \"%V\" is not found in
header", &name);
#endif
            goto secret_cookie_not_found;
        }

#if (NGX_DEBUG)
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: cookie \"%V\" found in header,
value=%V and length=%i",
                   &name, &submitted_sc, submitted_sc.len);
#endif

        if (submitted_sc.len != 2*SHA_DIGEST_LENGTH) {
#if (NGX_DEBUG)
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: size of cookie \"%V\" is incorrect",
&name);
#endif
            goto secret_cookie_not_found;
        }
        
        // User Agent can be very large. Limit to a certain size
        // to keep small memory footprint
        if ((sc[i].ua_limit > 0) && (user_agent.len > sc[i].ua_limit))
{
            ua_len  = sc[i].ua_limit;
        } else {
            ua_len = user_agent.len;
        }

        salt.len   = sc[i].salt.len;
        salt.data  = sc[i].salt.data;
        rule.len   = sc[i].rule.len;
        rule.data  = sc[i].rule.data;
        // Obtain current time in second
        timestamp = ngx_time();
        // Calculate the last_change and convert it to string
        last_change.data = ngx_pcalloc(r->pool, NGX_INT64_LEN);
        if (last_change.data == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                             "secret_cookie: Out of memory for
last_change string");
            return NULL;
        }
        p = ngx_num2str(last_change.data, 
                        last_change.data + NGX_INT64_LEN, 
                        (uint64_t) timestamp / sc[i].duration);
        last_change.len = p - last_change.data;

#if (NGX_DEBUG)
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: name has length=%i and value=%V",
name.len, &name);
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: salt has length=%i and value=%V",
salt.len, &salt);
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: rule has length=%i and value=%V",
rule.len, &rule);
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: duration = %i", sc[i].duration);
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: last_change = %V", &last_change);
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: remote_address has length=%i and
value=%V", 
                   remote_address.len, &remote_address);
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: user-agent has length=%i from
value=%V", 
                   ua_len, &user_agent);
#endif

        // Second, compute the secret_cookie according to the rule:
        sc_string.len = 0;
        for (n=0; n < rule.len; n++){
            switch (ngx_tolower(rule.data[n])){
                case NGX_HTTP_SECRET_COOKIE_RULE_ADDRESS:
                    sc_string.len += remote_address.len;
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_SALT:
                    sc_string.len += salt.len;
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_USER_AGENT:
                    sc_string.len += ua_len;
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_TIME:
                    sc_string.len += last_change.len;
                    break;
                default:
#if (NGX_DEBUG)
                    ngx_log_debug(NGX_LOG_DEBUG_HTTP,
r->connection->log, 0,
                       "secret_cookie: Unknown rule");
#endif
                    break;                    
            } // End of switch
        }

        sc_string.data = ngx_pcalloc(r->pool, sc_string.len);
        if (sc_string.data == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: Out of memory for sc_string
variable");
            return NULL;
        }
        p = sc_string.data;
        for (n=0; n < rule.len; n++){
            switch (ngx_tolower(rule.data[n])){
                case NGX_HTTP_SECRET_COOKIE_RULE_ADDRESS:
                    // This ngx_copy faster than ngx_cpymem  with buffer
< 16 bytes
                    p = ngx_copy(p, remote_address.data,
remote_address.len); 
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_SALT:
                    p = ngx_copy(p, salt.data, salt.len);
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_USER_AGENT:
                    if (ua_len > 0){
                        p = ngx_cpymem(p, user_agent.data, ua_len);
                    }
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_TIME:
                    p = ngx_copy(p, last_change.data, last_change.len);
                    break;
                default:
#if (NGX_DEBUG)
                    ngx_log_debug(NGX_LOG_DEBUG_HTTP,
r->connection->log, 0,
                       "secret_cookie: Unknown rule");
#endif 
                    break;                    
            } // End of switch
        }
#if (NGX_DEBUG)
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: String to compute secret_cookie has
length=%i and value=%V", 
                   sc_string.len, &sc_string);
#endif

        // Third, perform sha1 on the content
        computed_sc.len   =  SHA_DIGEST_LENGTH;
        computed_sc.data  =  ngx_pcalloc(r->pool, SHA_DIGEST_LENGTH);
        if (computed_sc.data == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: Out of memory for computed_sc
variable");
            return NULL;
        }
        n = ngx_sha1_init(&sha_ctx);
        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: SHA1 init fails");
            return NULL;
        }
        n = ngx_sha1_update(&sha_ctx, sc_string.data, sc_string.len);
        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: SHA1 update fails");
            return NULL;
        }
        n = ngx_sha1_final(computed_sc.data, &sha_ctx);
        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: SHA1 final fails");
            return NULL;
        }
        // Release the sc_string memory:
        
#if (NGX_DEBUG)
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: computed_secret_cookie has length=%i
and value=%V", SHA_DIGEST_LENGTH, &computed_sc);
#endif
        if (ngx_raw_vs_hex(computed_sc.data, SHA_DIGEST_LENGTH,
submitted_sc.data, 2*SHA_DIGEST_LENGTH) == 0){
            if (sc[i].log == NGX_HTTP_SECRET_COOKIE_ON){
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   "secret_cookie: cookie \"%V\" is valid", &name);
            }
            goto secret_cookie_found;
        } 
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                      "secret_cookie: cookie \"%V\" does not match",
&name);
#endif

secret_cookie_not_found:
        if (sc[i].log == NGX_HTTP_SECRET_COOKIE_ON){
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "secret_cookie: no valid cookie \"%V\" in header",
&name);
        }
        continue;

secret_cookie_found:
        // Append the found secret_cookie's name into
secret_cookie_value:
        p = ngx_copy(secret_cookie_value.data + secret_cookie_value.len,
name.data, name.len);
        *p = (u_char) NGX_HTTP_SECRET_COOKIE_DIVIDER; // Name is
separate by a space
        secret_cookie_value.len += name.len + 1;
    }
    // Setup the secret_cookie_value variable:
    ctx->secret_cookie_value->len          = secret_cookie_value.len;
    ctx->secret_cookie_value->data         = secret_cookie_value.data;
    ctx->secret_cookie_value->valid        = 1;
    ctx->secret_cookie_value->no_cacheable = 1;
    ctx->secret_cookie_value->not_found    = 0;

#if (NGX_DEBUG)
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
               "secret_cookie: The final secret_cookie_value has
length=%i and value=%V", secret_cookie_value.len,
&secret_cookie_value);
#endif
    // Return the result
    return ctx;
}

/* Add $secret_cookie_set variable to the http process.
 * Called by "preconfiguration" hook!
 */
static ngx_int_t
ngx_http_secret_cookie_add_variable(ngx_conf_t *cf)
{
    ngx_http_secret_cookie_variable_t   *var;
    ngx_http_variable_t                 *v;

    for (var = ngx_http_secret_cookie_vars; var->name.len; var++) {

        v = ngx_http_add_variable(cf, &var->name,
NGX_HTTP_VAR_CHANGEABLE);
        if (v == NULL) {
            return NGX_ERROR;
        }

        v->get_handler = var->handler;
        v->data = var->data;
    }

    return NGX_OK;
}

/*
 * This function is called when location configuration is created.
 * It will create a default configuration for secret_cookie module.
 */
static void *
ngx_http_secret_cookie_create_conf(ngx_conf_t *cf)
{
    ngx_http_secret_cookie_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool,
sizeof(ngx_http_secret_cookie_conf_t));
    if (conf == NULL) {
        return NULL;
    }

    /*
     * Set by ngx_pcalloc():
     * 
     *   conf->secret_cookies = NULL;
     *   conf->secret_cookies_value_disabled = NULL;
     */
    conf->status = NGX_CONF_UNSET;
    return conf;
}

/*
 * This function is called when location configuration is merged to main
config.
 * It will create a default configuration for secret_cookie module.
 */
static char *
ngx_http_secret_cookie_merge_conf(ngx_conf_t *cf, void *parent, void
*child)
{
    ngx_http_secret_cookie_conf_t  *prev = parent;
    ngx_http_secret_cookie_conf_t  *conf = child;
    u_char                         *p;
    ngx_str_t                      secret_cookie_value_disabled;
    ngx_uint_t                     i;
    ngx_http_secret_cookie_t       *sc;

    // Merge previous to current configration, with a default value:
    ngx_conf_merge_value(conf->status, prev->status,
                              NGX_HTTP_SECRET_COOKIE_OFF);
    if (conf->secret_cookies == NULL){
        conf->secret_cookies = prev->secret_cookies;
        conf->secret_cookie_value_disabled =
prev->secret_cookie_value_disabled;
    } else {
        // Generate the disabled_value for this configuration:
        // First, count the required string length
        secret_cookie_value_disabled.len = 0;
        sc = conf->secret_cookies->elts;
        for (i=0; isecret_cookies->nelts; i++){
            secret_cookie_value_disabled.len += sc[i].name.len + 1;
        }
        // Secondly, create the buffer containing disabled_value       

        secret_cookie_value_disabled.data = ngx_pcalloc(cf->pool,
secret_cookie_value_disabled.len);
        if (secret_cookie_value_disabled.data == NULL){
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
                        "Not enough memory for
secret_cookie_value_disabled string");
            return NGX_CONF_ERROR;
        }
        // Thirdly, copy all secret cookie's name to disabled_value
        p = secret_cookie_value_disabled.data;
        for (i=0; isecret_cookies->nelts; i++){
            p = ngx_copy(p, sc[i].name.data, sc[i].name.len);
            *p = (u_char) NGX_HTTP_SECRET_COOKIE_DIVIDER; // Name is
separate by a space
            p++;
        }
#if (NGX_DEBUG)
        ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
              "String secret_cookie_value_disabled has length = %i and
value = %V", secret_cookie_value_disabled.len,
&secret_cookie_value_disabled);
#endif
        conf->secret_cookie_value_disabled = ngx_pcalloc(cf->pool,
sizeof(ngx_http_variable_value_t));
        if (conf->secret_cookie_value_disabled == NULL){
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
                        "Not enough memory for
conf->secret_cookie_value_disabled variable");
            return NGX_CONF_ERROR;
        }
        conf->secret_cookie_value_disabled->len          =
secret_cookie_value_disabled.len;
        conf->secret_cookie_value_disabled->valid        = 1;
        conf->secret_cookie_value_disabled->no_cacheable = 1;
        conf->secret_cookie_value_disabled->not_found    = 0;
        conf->secret_cookie_value_disabled->data         =
secret_cookie_value_disabled.data;
    }

    return NGX_CONF_OK;
}

/*
 * This function is called after secret_cookie_def is passed 
 * from config to module. Here you can validate the param, or change the

 * param to meaningful value.
 */
static char *
ngx_http_secret_cookie_def(ngx_conf_t *cf, ngx_command_t *cmd, void
*conf)
{
    ngx_http_secret_cookie_conf_t *sccf = conf;

    ngx_uint_t                    n, i, anumber;
    u_char                        c;
    ngx_str_t                     *value;
    ngx_http_secret_cookie_t      *sc;

    // Create secret_cookies array to hold definitions
    if (sccf->secret_cookies == NULL){
        sccf->secret_cookies = ngx_array_create(cf->pool, 3, 
                                               
sizeof(ngx_http_secret_cookie_t));
        if (sccf->secret_cookies == NULL){
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                        "Not enough memory for sccf->secret_cookies
array");
            return NGX_CONF_ERROR;
        }
    }
    // Create new element in the array to hold one definition
    sc = ngx_array_push(sccf->secret_cookies);
    if (sc == NULL) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                "Not enough memory for new element in
sccf->secret_cookies array");
        return NGX_CONF_ERROR;
    }
    // Process the parameters
    sc->status = NGX_HTTP_SECRET_COOKIE_ON;
    sc->log = NGX_HTTP_SECRET_COOKIE_OFF;
    sc->ua_limit = 0;
    value = cf->args->elts;
    for (n=1; n < cf->args->nelts; n++){
        // off
        if (ngx_strncmp(value[n].data,"off",3)==0){
            sc->status = NGX_HTTP_SECRET_COOKIE_OFF;
            continue;
        } 
        // log
        if (ngx_strncmp(value[n].data,"log=on",6)==0){
            sc->log = NGX_HTTP_SECRET_COOKIE_ON;
            continue;
        }
        //ua_limit=
        if (ngx_strncmp(value[n].data, "ua_limit=", 9) == 0){
            // Convert ua_limit to number:
            anumber = 0;
            for (i=9; i < value[n].len; i++){
                c = value[n].data[i];
                if (c >= '0' && c <= '9'){
                    anumber = anumber*10 + (c - '0');
                    continue;
                }
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Invalid \"ua_limit\" (must be an integer)");
                return NGX_CONF_ERROR;
            }
            // Validate the ua_limit:
            if ((anumber > 0) && (anumber <
NGX_HTTP_SECRET_COOKIE_MINIMUM_UA_LIMIT)){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Invalid \"ua_limit\" (must be greater than
%i)", NGX_HTTP_SECRET_COOKIE_MINIMUM_UA_LIMIT);
                return NGX_CONF_ERROR;
            }
            sc->ua_limit = anumber;
            continue;
        }
        // name=
        if (ngx_strncmp(value[n].data, "name=", 5) == 0){
            sc->name.len  = value[n].len - 5;
            if ((sc->name.len < 1) || (sc->name.len >
NGX_HTTP_SECRET_COOKIE_MAX_SHORT_STR_SIZE)){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Parameter \"name\" is empty or too long");
                return NGX_CONF_ERROR;
            }
            sc->name.data = value[n].data + 5;
            continue;
        }
        // salt=
        if (ngx_strncmp(value[n].data, "salt=", 5) == 0){
            sc->salt.len  = value[n].len - 5;
            if ((sc->salt.len < 1) || (sc->salt.len >
NGX_HTTP_SECRET_COOKIE_MAX_SHORT_STR_SIZE)){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Parameter \"salt\" is empty or too long");
                return NGX_CONF_ERROR;
            }
            sc->salt.data = value[n].data + 5;
            continue;
        }
        // rule=
        if (ngx_strncmp(value[n].data, "rule=", 5) == 0){
            // Validate the rule:
            for (i=5; i < value[n].len; i++){
                c = ngx_tolower(value[n].data[i]);
                switch (c){
                    case NGX_HTTP_SECRET_COOKIE_RULE_ADDRESS:
                    case NGX_HTTP_SECRET_COOKIE_RULE_TIME:
                    case NGX_HTTP_SECRET_COOKIE_RULE_SALT:
                    case NGX_HTTP_SECRET_COOKIE_RULE_USER_AGENT:
                        break;
                    default:
                        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "Rule does not accept characters other than
\"a\", \"t\", \"s\" and \"u\"");
                        return NGX_CONF_ERROR;
                }
            }

            sc->rule.len  = value[n].len - 5;
            if ((sc->rule.len < 1) || (sc->rule.len >
NGX_HTTP_SECRET_COOKIE_MAX_SHORT_STR_SIZE)){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Parameter \"rule\" is empty or too long");
                return NGX_CONF_ERROR;
            }
            sc->rule.data = value[n].data + 5;
            continue;
        }
        // duration
        if (ngx_strncmp(value[n].data, "duration=", 9) == 0){
            // Convert duration to number:
            anumber = 0;
            for (i=9; i < value[n].len; i++){
                c = value[n].data[i];
                if (c >= '0' && c <= '9'){
                    anumber = anumber*10 + (c - '0');
                    continue;
                }
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Invalid \"duration\" (must be an integer)");
                return NGX_CONF_ERROR;
            }
            // Validate the duration:
            if (anumber < NGX_HTTP_SECRET_COOKIE_MINIMUM_DURATION){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Invalid \"duration\" (must be greater than
%i)", NGX_HTTP_SECRET_COOKIE_MINIMUM_DURATION);
                return NGX_CONF_ERROR;
            }
            sc->duration = anumber;
            continue;
        }
    }

    if (sc->rule.data == NULL){
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "The required parameter \"rule\" is
missing!");
        return NGX_CONF_ERROR;
    }
    if (sc->salt.data == NULL){
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "The required parameter \"salt\" is
missing!");
        return NGX_CONF_ERROR;
    }
    if (sc->name.data == NULL){
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "The required parameter \"name\" is
missing!");
        return NGX_CONF_ERROR;
    }
    if (sc->duration == 0){
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "The required parameter \"duration\" is
missing");
        return NGX_CONF_ERROR;
    }
#if (NGX_DEBUG)
    ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0,
                   "Secret_cookie_def: on/off=%i name=%V salt=%V rule=%V
duration=%i", 
                   sc->status, &sc->name, &sc->salt, &sc->rule,
sc->duration);
#endif
    return NGX_CONF_OK;
}

/*
 * Convert an int64 to string presentation
 */
static u_char * ngx_num2str(u_char *buf, u_char *last, uint64_t ui64)
{
    u_char         *p, temp[NGX_INT64_LEN + 1];
                       /*
                        * we need temp[NGX_INT64_LEN] only,
                        * but icc issues the warning
                        */
    size_t          len;
    uint32_t        ui32;

    p = temp + NGX_INT64_LEN;

    if (ui64 <= NGX_MAX_UINT32_VALUE) {
        ui32 = (uint32_t) ui64;

        do {
            *--p = (u_char) (ui32 % 10 + '0');
        } while (ui32 /= 10);

    } else {
        do {
            *--p = (u_char) (ui64 % 10 + '0');
        } while (ui64 /= 10);
    }

    /* number safe copy */

    len = (temp + NGX_INT64_LEN) - p;

    if (buf + len > last) {
        len = last - buf;
    }

    return ngx_cpymem(buf, p, len);
}

/*
 * Comparison of a raw string and a hex-style string
 */
static ngx_int_t ngx_raw_vs_hex(u_char *rawbuf, size_t rlen, u_char
*hexbuf, size_t hlen)
{
    static u_char    hex[] = "0123456789abcdef";
    u_char           hexchar[] = "aa";
    ngx_int_t        len, i;
    // Compare by the shortest length
    len = ((rlen < hlen / 2) ? rlen : hlen / 2);
    i = 0;
    while (i < len){
        // Calculate a hexchar from rawbuff
        hexchar[1] = hex[rawbuf[i] & 0xf];
        hexchar[0] = hex[(rawbuf[i]>>4) & 0xf];
        if ((hexchar[0] != hexbuf[2*i]) || (hexchar[1] !=
hexbuf[2*i+1])){
            return 1;
        }
        i++;
    }
    return 0;
}
[/code]

Posted at Nginx Forum: http://forum.nginx.org/read.php?2,55378,173796#msg-173796




More information about the nginx mailing list