7 changed files with 667 additions and 0 deletions
-
6configure.ac
-
1doc/config/conf.d/Makefile.am
-
101doc/config/conf.d/csrf.conf
-
5src/CMakeLists.txt
-
11src/Makefile.am
-
1src/SConscript
-
542src/mod_csrf.c
@ -0,0 +1,101 @@ |
|||
####################################################################### |
|||
## |
|||
## CSRF Protection Module |
|||
## ---------------- |
|||
## |
|||
## Make sure to load "mod_auth" before (or whatever is supposed to set |
|||
## REMOTE_USER). |
|||
## |
|||
server.modules += ( "mod_csrf" ) |
|||
|
|||
## Activate CSRF module: |
|||
## |
|||
## If module is activated and (REMOTE_USER not empty or |
|||
## csrf.require-user is disabled) the module makes sure the client |
|||
## receives a token unless it has a valid token which is less than |
|||
## csrf.ttl/4 seconds old |
|||
## |
|||
## Default: |
|||
# csrf.activate = "disable" |
|||
|
|||
## Use conditions to activate protection for certain URLs: |
|||
# $HTTP["url"] =~ "^/(someurl|cgi-bin)/(.+)" { |
|||
# csrf.activate = "enable" |
|||
# } |
|||
|
|||
## CSRF-protect all requests |
|||
## |
|||
## As soon as CSRF is activated all requests are by default protected |
|||
## (event GET requests), i.e. require the client to send a valid CSRF |
|||
## token. You can disable protection to just make sure the client |
|||
## receives a valid token for future requests. |
|||
## |
|||
## Default: |
|||
# csrf.protect = "enable" |
|||
|
|||
## Don't require CSRF for GET requests (but still send tokens in |
|||
## response) |
|||
# csrf.activate = "enable" |
|||
# $HTTP["request-method"] == "GET" { |
|||
# csrf.protect = "disable" |
|||
# } |
|||
|
|||
## Require a logged in user |
|||
## |
|||
## To prevent mistakes in the config by default a REMOTE_USER is |
|||
## required. If your users are authenticated in another way (say client |
|||
## ip address) and you don't have REMOTE_USER you still can use this |
|||
## module to prevent CSRF from external sites, but you need to disable |
|||
## this option. |
|||
## |
|||
## Default: |
|||
# csrf.require-user = "enable" |
|||
|
|||
## Activate debug logging |
|||
## |
|||
## Default: |
|||
# csrf.debug = "disable" |
|||
|
|||
## Hash function to use for HMAC |
|||
## |
|||
## Supports whatever your openssl library recognizes |
|||
## |
|||
## Default: |
|||
# csrf.hash = "sha256" |
|||
|
|||
## HTTP Header name for CSRF tokens |
|||
## |
|||
## Header name for both HTTP requests and HTTP responses. |
|||
## |
|||
## A client application needs to read this header from responses and |
|||
## copy it into new requests to gain access to protected resources. |
|||
## |
|||
## Default: |
|||
# csrf.header = "X-Csrf-Token" |
|||
|
|||
## Secret key for HMAC to "sign" token data with |
|||
## |
|||
## Only set this if you need tokens to stay valid across a load-balanced |
|||
## setup. If set needs to be at least 20 characters long. Use some |
|||
## secure "password" generator if you need this (e.g. "pwgen -s 32 1") |
|||
## |
|||
## Default: create a random 20-byte secret on each restart |
|||
# csrf.secret = "..." |
|||
|
|||
## Default Time-To-Live for a token |
|||
## |
|||
## How long (in seconds) a token is valid; after csrf.ttl/4 seconds the |
|||
## module will send the client a new token. |
|||
## |
|||
## Your client applications still need to be able to handle token |
|||
## timeouts (i.e. retry requests with the new token they received). |
|||
## |
|||
## A token will also be valid csrf.ttl seconds *before* its timestamp |
|||
## (to avoid problems with time sync between multiple nodes in a |
|||
## cluster) |
|||
## |
|||
## Default: (10 minutes) |
|||
# csrf.ttl = 600 |
|||
|
|||
## |
|||
####################################################################### |
@ -0,0 +1,542 @@ |
|||
#include "base.h" |
|||
#include "log.h" |
|||
#include "buffer.h" |
|||
#include "base64.h" |
|||
|
|||
#include "plugin.h" |
|||
|
|||
#include <ctype.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
|
|||
#include <openssl/rand.h> |
|||
#include <openssl/hmac.h> |
|||
|
|||
/* Token: |
|||
* |
|||
* the token protects a message + additional data: |
|||
* |
|||
* "message": |
|||
* - 1 byte: version (0x01) |
|||
* - 8 bytes: uint64_t big endian timestamp in seconds since epoch |
|||
* "additional data": |
|||
* - REMOTE_USER as simple string (without null termination); can be empty |
|||
* REMOTE_USER is usually set by mod_auth; you must load mod_auth before mod_csrf! |
|||
* |
|||
* The token consists of the base64-encoding of "message" and a checksum (HMAC) |
|||
*/ |
|||
|
|||
#define SECRET_SIZE_BYTES 20 /* secret to automatically generate as fallback */ |
|||
#define TOKEN_DEFAULT_TTL (10*60) /* in seconds: 10 minutes */ |
|||
#define TOKEN_DEFAULT_HEADER "X-Csrf-Token" |
|||
|
|||
typedef enum { |
|||
TOKEN_CHECK_OK, |
|||
TOKEN_CHECK_OK_RENEW, |
|||
TOKEN_CHECK_FAILED |
|||
} token_check_result; |
|||
|
|||
/* plugin config for all request/connections */ |
|||
|
|||
typedef struct { |
|||
unsigned short activate:1; |
|||
unsigned short protect:1; |
|||
unsigned short require_user:1; |
|||
unsigned short debug:1; |
|||
|
|||
int ttl; |
|||
|
|||
const EVP_MD* hash; |
|||
buffer* header; |
|||
buffer* secret; |
|||
} plugin_config; |
|||
|
|||
typedef struct { |
|||
PLUGIN_DATA; |
|||
|
|||
plugin_config* config_storage; |
|||
plugin_config conf; |
|||
} plugin_data; |
|||
|
|||
static buffer* create_message(void) { |
|||
uint64_t now = (uint64_t) time(NULL); |
|||
buffer* msg = buffer_init(); |
|||
|
|||
{ |
|||
char* raw = buffer_string_prepare_append(msg, 1 + sizeof(now)); |
|||
size_t i; |
|||
|
|||
*raw++ = 1; /* CSRF token "version" */ |
|||
/* store "now" as big endian */ |
|||
for (i = sizeof(now); i-- > 0; ) { |
|||
raw[i] = (char) (now); |
|||
now >>= 8; |
|||
} |
|||
buffer_commit(msg, 1 + sizeof(now)); |
|||
} |
|||
|
|||
return msg; |
|||
} |
|||
|
|||
/* returns 0 on failure */ |
|||
static int parse_and_check_message(server *srv, plugin_data* p, char const* msg, size_t msg_len, uint64_t* ts) { |
|||
if (0 == msg_len) { |
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"invalid token length: empty message"); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
if (msg[0] != 1) { |
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "sx", |
|||
"invalid token version, expected 0x01, got: ", |
|||
(int)(unsigned char)msg[0]); |
|||
} |
|||
return 0; /* CSRF token "version" check */ |
|||
} |
|||
|
|||
if (msg_len != 9) { |
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "sd", |
|||
"invalid token message length, expected 9, got: ", |
|||
(int) msg_len); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* parse timestamp */ |
|||
{ |
|||
uint64_t tsn = 0; |
|||
size_t i; |
|||
for (i = 1; i < 9; ++i) { |
|||
tsn = (tsn << 8) | (unsigned char)(msg[i]); |
|||
} |
|||
*ts = tsn; |
|||
} |
|||
|
|||
return 1; |
|||
} |
|||
|
|||
static HMAC_CTX* csrf_hmac_ctx_new(void) { |
|||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L |
|||
return HMAC_CTX_new(); |
|||
#else |
|||
HMAC_CTX* ctx = malloc(sizeof(HMAC_CTX)); |
|||
HMAC_CTX_init(&ctx); |
|||
return ctx; |
|||
#endif |
|||
} |
|||
|
|||
static void csrf_hmac_ctx_free(HMAC_CTX* ctx) { |
|||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L |
|||
HMAC_CTX_free(ctx); |
|||
#else |
|||
HMAC_CTX_cleanup(ctx); |
|||
free(ctx); |
|||
#endif |
|||
} |
|||
|
|||
|
|||
/* returns 0 on failure */ |
|||
static int hmac_message(plugin_data* p, char* digest, char const* msg, size_t msg_len, const char* user) { |
|||
HMAC_CTX* ctx = csrf_hmac_ctx_new(); |
|||
|
|||
force_assert(buffer_string_length(p->conf.secret) >= SECRET_SIZE_BYTES); |
|||
force_assert(NULL != p->conf.hash); |
|||
if (!HMAC_Init_ex(ctx, CONST_BUF_LEN(p->conf.secret), p->conf.hash, NULL)) goto err; |
|||
|
|||
if (!HMAC_Update(ctx, (unsigned char const*) msg, msg_len)) goto err; |
|||
/* message must be "self-terminating" and length-checked! |
|||
* -> just append user to message if there is one |
|||
*/ |
|||
if (user && !HMAC_Update(ctx, (unsigned char const*) user, strlen(user))) goto err; |
|||
if (!HMAC_Final(ctx, (unsigned char*) digest, NULL)) goto err; |
|||
|
|||
csrf_hmac_ctx_free(ctx); |
|||
return 1; |
|||
|
|||
err: |
|||
csrf_hmac_ctx_free(ctx); |
|||
return 0; |
|||
} |
|||
|
|||
/* returns 0 on failure */ |
|||
static int append_token(server* srv, plugin_data* p, buffer* buf, const char* user) { |
|||
buffer* msg = create_message(); |
|||
|
|||
{ |
|||
const size_t digest_len = EVP_MD_size(p->conf.hash); |
|||
char* digest = buffer_string_prepare_append(msg, digest_len); |
|||
if (!hmac_message(p, digest, CONST_BUF_LEN(msg), user)) { |
|||
buffer_free(msg); |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"failed to create token digest"); |
|||
return 0; |
|||
} |
|||
buffer_commit(msg, digest_len); |
|||
} |
|||
buffer_append_base64_encode_no_padding(buf, (unsigned char const*) CONST_BUF_LEN(msg), BASE64_STANDARD); |
|||
buffer_free(msg); |
|||
|
|||
return 1; |
|||
} |
|||
|
|||
static token_check_result verify_token(server* srv, plugin_data* p, char const* token, size_t token_len, const char* user) { |
|||
uint64_t ts; |
|||
|
|||
{ |
|||
const size_t digest_len = EVP_MD_size(p->conf.hash); |
|||
size_t msg_len; |
|||
buffer* decoded_token = buffer_init(); |
|||
|
|||
if (NULL == buffer_append_base64_decode(decoded_token, token, token_len, BASE64_STANDARD)) { |
|||
buffer_free(decoded_token); |
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"failed to decode base64 token"); |
|||
} |
|||
return TOKEN_CHECK_FAILED; |
|||
} |
|||
|
|||
if (buffer_string_length(decoded_token) < digest_len) { |
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"token too short for digest"); |
|||
} |
|||
return TOKEN_CHECK_FAILED; |
|||
} |
|||
msg_len = buffer_string_length(decoded_token) - digest_len; |
|||
|
|||
if (!parse_and_check_message(srv, p, decoded_token->ptr, msg_len, &ts)) { |
|||
buffer_free(decoded_token); |
|||
return TOKEN_CHECK_FAILED; |
|||
} |
|||
|
|||
{ |
|||
buffer* digest_buf = buffer_init(); |
|||
char* digest = buffer_string_prepare_append(digest_buf, digest_len); |
|||
if (!hmac_message(p, digest, decoded_token->ptr, msg_len, user) |
|||
|| 0 != strncmp(decoded_token->ptr + msg_len, (char const*) digest, digest_len)) |
|||
{ |
|||
buffer_free(decoded_token); |
|||
buffer_free(digest_buf); |
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"token digest didn't match"); |
|||
} |
|||
return TOKEN_CHECK_FAILED; |
|||
} |
|||
buffer_free(digest_buf); |
|||
} |
|||
buffer_free(decoded_token); |
|||
} |
|||
|
|||
{ |
|||
int64_t timediff = (int64_t)(ts - (uint64_t)time(NULL)); |
|||
/* accept "ttl" seconds in BOTH directions - usually you shouldn't sign |
|||
* too much ahead of time (in case of multiple servers) |
|||
*/ |
|||
if (timediff < -p->conf.ttl || timediff > p->conf.ttl) { |
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"token expired"); |
|||
} |
|||
return TOKEN_CHECK_FAILED; /* timeout */ |
|||
} |
|||
|
|||
if (timediff > p->conf.ttl / 4) return TOKEN_CHECK_OK_RENEW; |
|||
} |
|||
|
|||
return TOKEN_CHECK_OK; |
|||
} |
|||
|
|||
/* init the plugin data */ |
|||
INIT_FUNC(mod_csrf_init) { |
|||
plugin_data* p; |
|||
|
|||
p = calloc(1, sizeof(*p)); |
|||
|
|||
return p; |
|||
} |
|||
|
|||
/* detroy the plugin data */ |
|||
FREE_FUNC(mod_csrf_free) { |
|||
plugin_data* p = p_d; |
|||
|
|||
UNUSED(srv); |
|||
|
|||
if (!p) return HANDLER_GO_ON; |
|||
|
|||
if (p->config_storage) { |
|||
size_t i; |
|||
for (i = 0; i < srv->config_context->used; i++) { |
|||
plugin_config* s = &p->config_storage[i]; |
|||
|
|||
buffer_free(s->header); |
|||
buffer_free(s->secret); |
|||
} |
|||
free(p->config_storage); |
|||
} |
|||
|
|||
free(p); |
|||
|
|||
return HANDLER_GO_ON; |
|||
} |
|||
|
|||
/* handle plugin config and check values */ |
|||
|
|||
#define CSRF_CONFIG_ACTIVATE "csrf.activate" |
|||
#define CSRF_CONFIG_PROTECT "csrf.protect" |
|||
#define CSRF_CONFIG_REQUIRE_USER "csrf.require-user" |
|||
#define CSRF_CONFIG_DEBUG "csrf.debug" |
|||
#define CSRF_CONFIG_HASH "csrf.hash" |
|||
#define CSRF_CONFIG_HEADER "csrf.header" |
|||
#define CSRF_CONFIG_SECRET "csrf.secret" |
|||
#define CSRF_CONFIG_TTL "csrf.ttl" |
|||
|
|||
SETDEFAULTS_FUNC(mod_csrf_set_defaults) { |
|||
plugin_data* p = p_d; |
|||
size_t i = 0; |
|||
buffer* hash = buffer_init(); |
|||
|
|||
config_values_t cv[] = { |
|||
{ CSRF_CONFIG_ACTIVATE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ |
|||
{ CSRF_CONFIG_PROTECT, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ |
|||
{ CSRF_CONFIG_REQUIRE_USER, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ |
|||
{ CSRF_CONFIG_DEBUG, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ |
|||
{ CSRF_CONFIG_TTL, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ |
|||
{ CSRF_CONFIG_HASH, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ |
|||
{ CSRF_CONFIG_HEADER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ |
|||
{ CSRF_CONFIG_SECRET, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ |
|||
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } |
|||
}; |
|||
|
|||
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config)); |
|||
|
|||
for (i = 0; i < srv->config_context->used; i++) { |
|||
data_config const* config = (data_config const*)srv->config_context->data[i]; |
|||
plugin_config* s = &p->config_storage[i]; |
|||
|
|||
unsigned short activate = 0; |
|||
unsigned short protect = 1; /* protect by default (when activated) */ |
|||
/* empty user not allowed by default to prevent mistakes in mod_auth/mod_csrf ordering */ |
|||
unsigned short require_user = 1; |
|||
unsigned short debug = 0; |
|||
unsigned short ttl = TOKEN_DEFAULT_TTL; |
|||
|
|||
s->hash = NULL; |
|||
s->header = buffer_init(); |
|||
s->secret = buffer_init(); |
|||
|
|||
buffer_reset(hash); |
|||
|
|||
cv[0].destination = &(activate); |
|||
cv[1].destination = &(protect); |
|||
cv[2].destination = &(require_user); |
|||
cv[3].destination = &(debug); |
|||
cv[4].destination = &(ttl); |
|||
cv[5].destination = hash; |
|||
cv[6].destination = s->header; |
|||
cv[7].destination = s->secret; |
|||
|
|||
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { |
|||
return HANDLER_ERROR; |
|||
} |
|||
|
|||
s->activate = activate; |
|||
s->protect = protect; |
|||
s->require_user = require_user; |
|||
s->debug = debug; |
|||
s->ttl = ttl; |
|||
|
|||
if (!buffer_is_empty(s->secret) && buffer_string_length(s->secret) < SECRET_SIZE_BYTES) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
CSRF_CONFIG_SECRET " too short"); |
|||
return HANDLER_ERROR; |
|||
} |
|||
|
|||
if (!buffer_is_empty(hash)) { |
|||
s->hash = EVP_get_digestbyname(hash->ptr); |
|||
if (NULL == s->hash) { |
|||
log_error_write(srv, __FILE__, __LINE__, "sb", |
|||
"couldn't find " CSRF_CONFIG_HASH ":", |
|||
hash); |
|||
return HANDLER_ERROR; |
|||
} |
|||
} |
|||
} |
|||
|
|||
{ |
|||
plugin_config* s = &p->config_storage[0]; |
|||
|
|||
if (NULL == s->hash) s->hash = EVP_sha256(); |
|||
if (buffer_string_is_empty(s->header)) buffer_copy_string_len(s->header, CONST_STR_LEN(TOKEN_DEFAULT_HEADER)); |
|||
if (buffer_string_is_empty(s->secret)) { |
|||
buffer_string_set_length(s->secret, SECRET_SIZE_BYTES); |
|||
|
|||
if (RAND_bytes((unsigned char*) s->secret->ptr, SECRET_SIZE_BYTES) == 0) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"failed to generate secret key"); |
|||
return HANDLER_ERROR; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return HANDLER_GO_ON; |
|||
} |
|||
|
|||
#define PATCH(x) \ |
|||
p->conf.x = s->x; |
|||
static int mod_csrf_patch_connection(server* srv, connection* con, plugin_data* p) { |
|||
size_t i, j; |
|||
plugin_config* s = &p->config_storage[0]; |
|||
|
|||
PATCH(activate); |
|||
PATCH(protect); |
|||
PATCH(require_user); |
|||
PATCH(debug); |
|||
PATCH(hash); |
|||
PATCH(header); |
|||
PATCH(secret); |
|||
PATCH(ttl); |
|||
|
|||
/* skip the first, the global context */ |
|||
for (i = 1; i < srv->config_context->used; i++) { |
|||
data_config* dc = (data_config*) srv->config_context->data[i]; |
|||
s = &p->config_storage[i]; |
|||
|
|||
/* condition didn't match */ |
|||
if (!config_check_cond(srv, con, dc)) continue; |
|||
|
|||
/* merge config */ |
|||
for (j = 0; j < dc->value->used; j++) { |
|||
data_unset* du = dc->value->data[j]; |
|||
|
|||
if (buffer_is_equal_string(du->key, CONST_STR_LEN(CSRF_CONFIG_ACTIVATE))) { |
|||
PATCH(activate); |
|||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CSRF_CONFIG_PROTECT))) { |
|||
PATCH(protect); |
|||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CSRF_CONFIG_REQUIRE_USER))) { |
|||
PATCH(require_user); |
|||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CSRF_CONFIG_DEBUG))) { |
|||
PATCH(debug); |
|||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CSRF_CONFIG_HASH))) { |
|||
PATCH(hash); |
|||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CSRF_CONFIG_HEADER))) { |
|||
PATCH(header); |
|||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CSRF_CONFIG_SECRET))) { |
|||
PATCH(secret); |
|||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CSRF_CONFIG_TTL))) { |
|||
PATCH(ttl); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
#undef PATCH |
|||
|
|||
URIHANDLER_FUNC(mod_csrf_uri_handler) { |
|||
plugin_data* p = p_d; |
|||
buffer* csrf_req_header = NULL; |
|||
const char* user = NULL; |
|||
token_check_result result = TOKEN_CHECK_FAILED; |
|||
|
|||
mod_csrf_patch_connection(srv, con, p); |
|||
|
|||
if (!p->conf.activate) return HANDLER_GO_ON; |
|||
|
|||
{ |
|||
data_string* ds_user = (data_string*) array_get_element(con->environment, "REMOTE_USER"); |
|||
if (NULL != ds_user) user = ds_user->value->ptr; |
|||
} |
|||
|
|||
if (p->conf.require_user && (NULL == user || 0 == strlen(user))) { |
|||
if (p->conf.protect) { |
|||
con->http_status = 403; |
|||
con->mode = DIRECT; |
|||
if (p->conf.debug || con->conf.log_request_handling) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"require user to protect with csrf: user is missing -> rejecting request"); |
|||
} |
|||
return HANDLER_FINISHED; |
|||
} else { |
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"require user to activate csrf: user is missing -> not generating token"); |
|||
} |
|||
/* only activate when we actually have a user */ |
|||
return HANDLER_GO_ON; |
|||
} |
|||
} |
|||
|
|||
{ |
|||
data_string* ds_req_header = (data_string*) array_get_element(con->request.headers, p->conf.header->ptr); |
|||
if (NULL != ds_req_header) csrf_req_header = ds_req_header->value; |
|||
} |
|||
|
|||
if (csrf_req_header) { |
|||
result = verify_token(srv, p, CONST_BUF_LEN(csrf_req_header), user); |
|||
} |
|||
|
|||
switch (result) { |
|||
case TOKEN_CHECK_OK: |
|||
break; |
|||
default: |
|||
{ |
|||
data_string* ds_resp_header = data_response_init(); |
|||
|
|||
if (p->conf.debug) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"old/invalid csrf token: sending new token"); |
|||
} |
|||
|
|||
buffer_copy_buffer(ds_resp_header->key, p->conf.header); |
|||
if (!append_token(srv, p, ds_resp_header->value, user)) { |
|||
ds_resp_header->free((data_unset*) ds_resp_header); |
|||
} else { |
|||
array_insert_unique(con->response.headers, (data_unset*) ds_resp_header); |
|||
} |
|||
} |
|||
break; |
|||
} |
|||
|
|||
switch (result) { |
|||
case TOKEN_CHECK_OK: |
|||
case TOKEN_CHECK_OK_RENEW: |
|||
if (p->conf.debug || con->conf.log_request_handling) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"valid csrf token: accepting request"); |
|||
} |
|||
break; |
|||
default: |
|||
if (p->conf.protect) { |
|||
con->http_status = 403; |
|||
con->mode = DIRECT; |
|||
if (p->conf.debug || con->conf.log_request_handling) { |
|||
log_error_write(srv, __FILE__, __LINE__, "s", |
|||
"missing/invalid csrf token: rejecting request"); |
|||
} |
|||
return HANDLER_FINISHED; |
|||
} |
|||
break; |
|||
} |
|||
return HANDLER_GO_ON; |
|||
} |
|||
|
|||
int mod_csrf_plugin_init(plugin* p); |
|||
int mod_csrf_plugin_init(plugin* p) { |
|||
p->version = LIGHTTPD_VERSION_ID; |
|||
p->name = buffer_init_string("csrf"); |
|||
|
|||
p->init = mod_csrf_init; |
|||
p->handle_uri_clean = mod_csrf_uri_handler; |
|||
p->set_defaults = mod_csrf_set_defaults; |
|||
p->cleanup = mod_csrf_free; |
|||
|
|||
p->data = NULL; |
|||
|
|||
return 0; |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue