blob: f4bbb522bef4b6db116e3835fa07b3e0d9738a39 [file] [log] [blame]
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#define NGX_HTTP_USERID_OFF 0x0002
#define NGX_HTTP_USERID_ON 0x0004
#define NGX_HTTP_USERID_LOGONLY 0x0008
#define NGX_HTTP_USERID_TIME 0x0010
typedef struct {
ngx_flag_t enable;
ngx_int_t version;
ngx_int_t service;
ngx_str_t name;
ngx_str_t domain;
ngx_str_t path;
time_t expires;
ngx_int_t p3p;
ngx_str_t p3p_string;
} ngx_http_userid_conf_t;
typedef struct {
uint32_t uid_got[4];
uint32_t uid_set[4];
struct timeval tv;
} ngx_http_userid_ctx_t;
static ngx_int_t ngx_http_userid_get_uid(ngx_http_request_t *r,
ngx_http_userid_ctx_t *ctx,
ngx_http_userid_conf_t *conf);
static u_char *ngx_http_userid_log_uid_got(ngx_http_request_t *r, u_char *buf,
uintptr_t data);
static u_char *ngx_http_userid_log_uid_set(ngx_http_request_t *r, u_char *buf,
uintptr_t data);
static u_char *ngx_http_userid_log_uid_time(ngx_http_request_t *r, u_char *buf,
uintptr_t data);
static ngx_int_t ngx_http_userid_pre_conf(ngx_conf_t *cf);
static void *ngx_http_userid_create_conf(ngx_conf_t *cf);
static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_http_userid_init(ngx_cycle_t *cycle);
static ngx_conf_enum_t ngx_http_userid_mask[] = {
{ ngx_string("off"), NGX_HTTP_USERID_OFF },
{ ngx_string("on"), NGX_HTTP_USERID_ON },
{ ngx_string("logonly"), NGX_HTTP_USERID_LOGONLY },
{ ngx_string("time"), NGX_HTTP_USERID_TIME },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_http_userid_commands[] = {
{ ngx_string("userid"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_userid_conf_t, enable),
ngx_http_userid_mask},
{ ngx_string("userid_service"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_userid_conf_t, service),
NULL},
{ ngx_string("userid_name"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_userid_conf_t, name),
NULL},
{ ngx_string("userid_domain"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_userid_conf_t, domain),
NULL},
{ ngx_string("userid_path"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_userid_conf_t, path),
NULL},
{ ngx_string("userid_expires"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_sec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_userid_conf_t, expires),
NULL},
ngx_null_command
};
ngx_http_module_t ngx_http_userid_module_ctx = {
ngx_http_userid_pre_conf, /* pre conf */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_userid_create_conf, /* create location configration */
ngx_http_userid_merge_conf /* merge location configration */
};
ngx_module_t ngx_http_userid_module = {
NGX_MODULE,
&ngx_http_userid_module_ctx, /* module context */
ngx_http_userid_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
ngx_http_userid_init, /* init module */
NULL /* init process */
};
static ngx_http_log_op_name_t ngx_http_userid_log_fmt_ops[] = {
{ ngx_string("uid_got"), 0, ngx_http_userid_log_uid_got },
{ ngx_string("uid_set"), 0, ngx_http_userid_log_uid_set },
{ ngx_string("uid_time"), TIME_T_LEN + 4, ngx_http_userid_log_uid_time },
{ ngx_null_string, 0, NULL }
};
static ngx_int_t ngx_http_userid_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
struct timeval tv;
ngx_http_userid_ctx_t *ctx;
ngx_http_userid_conf_t *conf;
conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_module);
if (conf->enable & NGX_HTTP_USERID_OFF) {
return NGX_OK;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_userid_module);
if (ctx) {
return NGX_OK;
}
ngx_http_create_ctx(r, ctx, ngx_http_userid_module,
sizeof(ngx_http_userid_ctx_t),
NGX_HTTP_INTERNAL_SERVER_ERROR);
if (conf->enable & (NGX_HTTP_USERID_ON|NGX_HTTP_USERID_LOGONLY)) {
rc = ngx_http_userid_get_uid(r, ctx, conf);
if (rc != NGX_OK) {
return rc;
}
}
if (conf->enable & NGX_HTTP_USERID_TIME) {
ngx_gettimeofday(&ctx->tv);
}
return NGX_OK;
}
static ngx_int_t ngx_http_userid_get_uid(ngx_http_request_t *r,
ngx_http_userid_ctx_t *ctx,
ngx_http_userid_conf_t *conf)
{
u_char *start, *last, *end;
uint32_t *uid;
ngx_int_t rc;
ngx_uint_t *cookies, i;
ngx_str_t src, dst;
ngx_table_elt_t *headers;
headers = r->headers_in.headers.elts;
cookies = r->headers_in.cookies.elts;
for (i = 0; i < r->headers_in.cookies.nelts; i++) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"cookie: %d:\"%s\"",
cookies[i],
headers[cookies[i]].value.data);
end = headers[cookies[i]].value.data + headers[cookies[i]].value.len;
for (start = headers[cookies[i]].value.data; start < end; /* void */) {
if (conf->name.len >= headers[cookies[i]].value.len
|| ngx_strncmp(start, conf->name.data, conf->name.len) != 0)
{
start += conf->name.len;
while (start < end && *start++ != ';') { /* void */ }
for (/* void */; start < end && *start == ' '; start++) { /**/ }
continue;
}
for (start += conf->name.len; start < end && *start == ' '; start++)
{
/* void */
}
if (*start != '=') {
break;
}
for (start++; start < end && *start == ' '; start++) { /* void */ }
for (last = start; last < end && *last != ';'; last++) { /**/ }
if (last - start < 22) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent too short userid cookie \"%s\"",
headers[cookies[i]].value.data);
break;
}
/*
* we have to limit encoded string to 22 characters
* because there are already the millions cookies with a garbage
* instead of the correct base64 trail "=="
*/
src.len = 22;
src.data = start;
rc = ngx_decode_base64(r->pool, &src, &dst);
if (rc == NGX_ABORT) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (rc == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent invalid userid cookie \"%s\"",
headers[cookies[i]].value.data);
break;
}
uid = (uint32_t *) dst.data;
ctx->uid_got[0] = uid[0];
ctx->uid_got[1] = uid[1];
ctx->uid_got[2] = uid[2];
ctx->uid_got[3] = uid[3];
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"uid: %08X%08X%08X%08X",
uid[0], uid[1], uid[2], uid[3]);
return NGX_OK;
}
}
return NGX_OK;
}
static u_char *ngx_http_userid_log_uid_got(ngx_http_request_t *r, u_char *buf,
uintptr_t data)
{
ngx_http_userid_ctx_t *ctx;
ngx_http_userid_conf_t *conf;
ctx = ngx_http_get_module_ctx(r, ngx_http_userid_module);
if (ctx == NULL || ctx->uid_got[3] == 0) {
if (buf == NULL) {
return (u_char *) 1;
}
*buf = '-';
return buf + 1;
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_module);
if (buf == NULL) {
return (u_char *) (conf->name.len + 1 + 32);
}
buf = ngx_cpymem(buf, conf->name.data, conf->name.len);
*buf++ = '=';
return buf + ngx_snprintf((char *) buf, 33, "%08X%08X%08X%08X",
ctx->uid_got[0], ctx->uid_got[1],
ctx->uid_got[2], ctx->uid_got[3]);
}
static u_char *ngx_http_userid_log_uid_set(ngx_http_request_t *r, u_char *buf,
uintptr_t data)
{
if (buf == NULL) {
return (u_char *) 1;
}
*buf = '-';
return buf + 1;
}
static u_char *ngx_http_userid_log_uid_time(ngx_http_request_t *r, u_char *buf,
uintptr_t data)
{
ngx_http_userid_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_userid_module);
if (ctx == NULL || ctx->tv.tv_sec == 0) {
*buf = '-';
return buf + 1;
}
return buf + ngx_snprintf((char *) buf, TIME_T_LEN + 5,
"%ld.%03ld",
ctx->tv.tv_sec, ctx->tv.tv_usec / 1000);
}
static ngx_int_t ngx_http_userid_pre_conf(ngx_conf_t *cf)
{
ngx_http_log_op_name_t *op;
for (op = ngx_http_userid_log_fmt_ops; op->name.len; op++) { /* void */ }
op->op = NULL;
op = ngx_http_log_fmt_ops;
for (op = ngx_http_log_fmt_ops; op->op; op++) {
if (op->name.len == 0) {
op = (ngx_http_log_op_name_t *) op->op;
}
}
op->op = (ngx_http_log_op_pt) ngx_http_userid_log_fmt_ops;
return NGX_OK;
}
static void *ngx_http_userid_create_conf(ngx_conf_t *cf)
{
ngx_http_userid_conf_t *conf;
if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t)))) {
return NGX_CONF_ERROR;
}
/* set by ngx_pcalloc():
conf->enable = 0;
conf->name.len = 0;
conf->name.date = NULL;
conf->domain.len = 0;
conf->domain.date = NULL;
conf->path.len = 0;
conf->path.date = NULL;
*/
return conf;
}
static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,
void *child)
{
ngx_http_userid_conf_t *prev = parent;
ngx_http_userid_conf_t *conf = child;
ngx_conf_merge_bitmask_value(conf->enable, prev->enable,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_USERID_OFF));
ngx_conf_merge_str_value(conf->name, prev->name, "uid");
ngx_conf_merge_str_value(conf->domain, prev->domain, ".");
ngx_conf_merge_str_value(conf->path, prev->path, "/");
return NGX_CONF_OK;
}
static ngx_int_t ngx_http_userid_init(ngx_cycle_t *cycle)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module);
h = ngx_push_array(&cmcf->phases[NGX_HTTP_MISC_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_userid_handler;
return NGX_OK;
}