Kore now supports GET parameters and automatic validation of GET/POST parameters.

Kore will automatically removes invalid parameters as a security measure.

See modules/examples/module.conf for an example of how this works.
This commit is contained in:
Joris Vink 2013-11-10 15:17:15 +01:00
parent 34c2f31a93
commit 1e250c1473
8 changed files with 293 additions and 30 deletions

View File

@ -53,8 +53,9 @@ struct http_file {
#define HTTP_METHOD_GET 0
#define HTTP_METHOD_POST 1
#define HTTP_REQUEST_COMPLETE 0x01
#define HTTP_REQUEST_DELETE 0x02
#define HTTP_REQUEST_COMPLETE 0x01
#define HTTP_REQUEST_DELETE 0x02
#define HTTP_REQUEST_PARSED_PARAMS 0x04
struct http_request {
u_int8_t method;

View File

@ -157,6 +157,14 @@ struct connection {
TAILQ_ENTRY(connection) flush_list;
};
struct kore_handler_params {
char *name;
u_int8_t method;
struct kore_validator *validator;
TAILQ_ENTRY(kore_handler_params) list;
};
#define HANDLER_TYPE_STATIC 1
#define HANDLER_TYPE_DYNAMIC 2
@ -168,6 +176,7 @@ struct kore_module_handle {
int errors;
regex_t rctx;
TAILQ_HEAD(, kore_handler_params) params;
TAILQ_ENTRY(kore_module_handle) list;
};
@ -356,6 +365,8 @@ void kore_validator_init(void);
void kore_validator_reload(void);
int kore_validator_add(char *, u_int8_t, char *);
int kore_validator_run(char *, char *);
int kore_validator_check(struct kore_validator *, char *);
struct kore_validator *kore_validator_lookup(char *);
void fatal(const char *, ...);
void kore_debug_internal(char *, int, const char *, ...);

View File

@ -96,7 +96,7 @@ domain localhost {
certkey cert/server.key
accesslog /var/log/kore_access.log
# Page handlers
# Static page handlers
static /css/style.css serve_style_css
static / serve_index
static /intro.jpg serve_intro
@ -105,6 +105,28 @@ domain localhost {
static /upload serve_file_upload
static /lock-test serve_lock_test
static /validator serve_validator
#
# We can use a dynamic handler to accept GET parameters.
#
dynamic ^/params-test(\??)[A-Z0-9a-z=&]*$ serve_params_test
# Configure /params-test POST to only accept the following parameters.
# They are automatically tested against the validator listed.
# If the validator would fail Kore will automatically remove the
# failing parameter, indicating something was wrong with it.
# Any parameters not present in the params block are also filtered out.
params post ^/params-test(\??)[A-Z0-9a-z=&]*$ {
validate test1 v_example
validate test2 v_regex
}
# Configure a GET parameter that /params-test can received. As before
# this is validated against the validator and removed if validation
# fails. All extra parameters in the GET query are filtered out.
params get ^/params-test(\??)[A-Z0-9a-z=&]*$ {
validate arg1 v_example
}
}
#domain domain.com {

View File

@ -27,6 +27,7 @@ int serve_spdyreset(struct http_request *);
int serve_file_upload(struct http_request *);
int serve_lock_test(struct http_request *);
int serve_validator(struct http_request *);
int serve_params_test(struct http_request *);
void my_callback(void);
int v_example_func(char *);
@ -227,6 +228,72 @@ serve_validator(struct http_request *req)
return (http_response(req, 200, (u_int8_t *)"OK", 2));
}
int
serve_params_test(struct http_request *req)
{
struct kore_buf *b;
u_int8_t *d;
u_int32_t len;
int r, i;
char *test, name[10];
b = kore_buf_create(static_len_html_params);
kore_buf_append(b, static_html_params, static_len_html_params);
/*
* The GET parameters will be filtered out on POST.
*/
if (http_argument_lookup(req, "arg1", &test)) {
kore_buf_replace_string(b, "$arg1$", test, strlen(test));
kore_mem_free(test);
} else {
kore_buf_replace_string(b, "$arg1$", NULL, 0);
}
if (http_argument_lookup(req, "$arg2$", &test)) {
kore_buf_replace_string(b, "$arg2$", test, strlen(test));
kore_mem_free(test);
} else {
kore_buf_replace_string(b, "$arg2$", NULL, 0);
}
if (req->method == HTTP_METHOD_GET) {
kore_buf_replace_string(b, "$test1$", NULL, 0);
kore_buf_replace_string(b, "$test2$", NULL, 0);
kore_buf_replace_string(b, "$test3$", NULL, 0);
http_response_header_add(req, "content-type", "text/html");
d = kore_buf_release(b, &len);
r = http_response(req, 200, d, len);
kore_mem_free(d);
return (r);
}
/*
* No need to call http_populate_arguments(), it's been done
* by the parameter validation automatically.
*/
for (i = 1; i < 4; i++) {
snprintf(name, sizeof(name), "test%d", i);
if (http_argument_lookup(req, name, &test)) {
snprintf(name, sizeof(name), "$test%d$", i);
kore_buf_replace_string(b, name, test, strlen(test));
kore_mem_free(test);
} else {
snprintf(name, sizeof(name), "$test%d$", i);
kore_buf_replace_string(b, name, NULL, 0);
}
}
http_response_header_add(req, "content-type", "text/html");
d = kore_buf_release(b, &len);
r = http_response(req, 200, d, len);
kore_mem_free(d);
return (r);
}
void
my_callback(void)
{

View File

@ -47,6 +47,8 @@ static int configure_http_postbody_max(char **);
static int configure_http_hsts_enable(char **);
static int configure_http_keepalive_time(char **);
static int configure_validator(char **);
static int configure_params(char **);
static int configure_validate(char **);
static void domain_sslstart(void);
static struct {
@ -79,11 +81,15 @@ static struct {
{ "http_hsts_enable", configure_http_hsts_enable },
{ "http_keepalive_time", configure_http_keepalive_time },
{ "validator", configure_validator },
{ "params", configure_params },
{ "validate", configure_validate },
{ NULL, NULL },
};
char *config_file = NULL;
static struct kore_domain *current_domain = NULL;
char *config_file = NULL;
static u_int8_t current_method = 0;
static struct kore_domain *current_domain = NULL;
static struct kore_module_handle *current_handler = NULL;
void
kore_parse_config(void)
@ -115,6 +121,12 @@ kore_parse_config(void)
*t = ' ';
}
if (!strcmp(p, "}") && current_handler != NULL) {
lineno++;
current_handler = NULL;
continue;
}
if (!strcmp(p, "}") && current_domain != NULL)
domain_sslstart();
@ -626,6 +638,78 @@ configure_validator(char **argv)
return (KORE_RESULT_OK);
}
static int
configure_params(char **argv)
{
struct kore_module_handle *hdlr;
if (current_domain == NULL) {
printf("params keyword used in wrong context\n");
return (KORE_RESULT_ERROR);
}
if (current_handler != NULL) {
printf("previous params block not closed\n");
return (KORE_RESULT_ERROR);
}
if (argv[2] == NULL)
return (KORE_RESULT_ERROR);
if (!strcasecmp(argv[1], "post")) {
current_method = HTTP_METHOD_POST;
} else if (!strcasecmp(argv[1], "get")) {
current_method = HTTP_METHOD_GET;
} else {
printf("unknown method: %s in params block for %s\n",
argv[1], argv[2]);
return (KORE_RESULT_ERROR);
}
/*
* Find the handler ourselves, otherwise the regex is applied
* in case of a dynamic page.
*/
TAILQ_FOREACH(hdlr, &(current_domain->handlers), list) {
if (!strcmp(hdlr->path, argv[2])) {
current_handler = hdlr;
return (KORE_RESULT_OK);
}
}
printf("params for unknown page handler: %s\n", argv[2]);
return (KORE_RESULT_ERROR);
}
static int
configure_validate(char **argv)
{
struct kore_handler_params *p;
struct kore_validator *val;
if (current_handler == NULL) {
printf("validate keyword used in wrong context\n");
return (KORE_RESULT_ERROR);
}
if (argv[2] == NULL)
return (KORE_RESULT_ERROR);
if ((val = kore_validator_lookup(argv[2])) == NULL) {
printf("unknown validator %s for %s\n", argv[2], argv[1]);
return (KORE_RESULT_ERROR);
}
p = kore_malloc(sizeof(*p));
p->validator = val;
p->method = current_method;
p->name = kore_strdup(argv[1]);
TAILQ_INSERT_TAIL(&(current_handler->params), p, list);
return (KORE_RESULT_OK);
}
static void
domain_sslstart(void)
{

View File

@ -25,6 +25,8 @@
static char *http_status_text(int);
static int http_post_data_recv(struct netbuf *);
static void http_validate_params(struct http_request *,
struct kore_module_handle *);
static TAILQ_HEAD(, http_request) http_requests;
static struct kore_pool http_request_pool;
@ -134,8 +136,13 @@ http_process(void)
if (hdlr == NULL) {
r = http_generic_404(req);
} else {
cb = hdlr->addr;
if (!TAILQ_EMPTY(&(hdlr->params)) &&
!(req->flags & HTTP_REQUEST_PARSED_PARAMS)) {
http_validate_params(req, hdlr);
req->flags |= HTTP_REQUEST_PARSED_PARAMS;
}
cb = hdlr->addr;
worker->active_hdlr = hdlr;
r = cb(req);
worker->active_hdlr = NULL;
@ -495,13 +502,15 @@ http_populate_arguments(struct http_request *req)
{
u_int32_t len;
int i, v, c, count;
char *query, *args[HTTP_MAX_QUERY_ARGS], *val[3];
char *p, *query, *args[HTTP_MAX_QUERY_ARGS], *val[3];
if (req->method == HTTP_METHOD_POST) {
query = http_post_data_text(req);
} else {
kore_debug("HTTP_METHOD_GET not supported for arguments");
return (0);
if ((p = strchr(req->path, '?')) == NULL)
return (0);
p++;
query = kore_strdup(p);
}
count = 0;
@ -868,6 +877,54 @@ http_post_data_recv(struct netbuf *nb)
return (KORE_RESULT_OK);
}
static void
http_validate_params(struct http_request *req, struct kore_module_handle *hdlr)
{
int r;
struct kore_handler_params *p;
struct http_arg *q, *next;
http_populate_arguments(req);
for (q = TAILQ_FIRST(&(req->arguments)); q != NULL; q = next) {
next = TAILQ_NEXT(q, list);
p = NULL;
TAILQ_FOREACH(p, &(hdlr->params), list) {
if (p->method != req->method)
continue;
if (!strcmp(p->name, q->name))
break;
}
if (q->value != NULL)
http_argument_urldecode(q->value);
r = KORE_RESULT_ERROR;
if (p != NULL && q->value != NULL) {
r = kore_validator_check(p->validator, q->value);
if (r != KORE_RESULT_OK) {
kore_log(LOG_NOTICE,
"validator %s(%s) for %s failed",
p->validator->name, p->name, req->path);
}
} else if (p == NULL) {
kore_log(LOG_NOTICE,
"received unexpected parameter %s for %s",
q->name, req->path);
}
if (r == KORE_RESULT_ERROR) {
TAILQ_REMOVE(&(req->arguments), q, list);
kore_mem_free(q->name);
if (q->value != NULL)
kore_mem_free(q->value);
kore_mem_free(q);
continue;
}
}
}
static char *
http_status_text(int status)
{

View File

@ -133,11 +133,12 @@ kore_module_handler_new(char *path, char *domain, char *func, int type)
hdlr->errors = 0;
hdlr->addr = addr;
hdlr->type = type;
TAILQ_INIT(&(hdlr->params));
hdlr->path = kore_strdup(path);
hdlr->func = kore_strdup(func);
if (hdlr->type == HANDLER_TYPE_DYNAMIC) {
if (regcomp(&(hdlr->rctx), hdlr->path, REG_NOSUB)) {
if (regcomp(&(hdlr->rctx), hdlr->path, REG_EXTENDED | REG_NOSUB)) {
kore_mem_free(hdlr->func);
kore_mem_free(hdlr->path);
kore_mem_free(hdlr);

View File

@ -34,7 +34,7 @@ kore_validator_add(char *name, u_int8_t type, char *arg)
switch (val->type) {
case KORE_VALIDATOR_TYPE_REGEX:
if (regcomp(&(val->rctx), arg, REG_NOSUB)) {
if (regcomp(&(val->rctx), arg, REG_EXTENDED | REG_NOSUB)) {
kore_mem_free(val);
kore_log(LOG_NOTICE,
"validator %s has bad regex %s", name, arg);
@ -65,36 +65,43 @@ kore_validator_add(char *name, u_int8_t type, char *arg)
int
kore_validator_run(char *name, char *data)
{
int r;
struct kore_validator *val;
TAILQ_FOREACH(val, &validators, list) {
if (strcmp(val->name, name))
continue;
switch (val->type) {
case KORE_VALIDATOR_TYPE_REGEX:
if (!regexec(&(val->rctx), data, 0, NULL, 0))
r = KORE_RESULT_OK;
else
r = KORE_RESULT_ERROR;
break;
case KORE_VALIDATOR_TYPE_FUNCTION:
r = val->func(data);
break;
default:
r = KORE_RESULT_ERROR;
kore_log(LOG_NOTICE, "invalid type %d for validator %s",
val->type, val->name);
break;
}
return (r);
return (kore_validator_check(val, data));
}
return (KORE_RESULT_ERROR);
}
int
kore_validator_check(struct kore_validator *val, char *data)
{
int r;
switch (val->type) {
case KORE_VALIDATOR_TYPE_REGEX:
if (!regexec(&(val->rctx), data, 0, NULL, 0))
r = KORE_RESULT_OK;
else
r = KORE_RESULT_ERROR;
break;
case KORE_VALIDATOR_TYPE_FUNCTION:
r = val->func(data);
break;
default:
r = KORE_RESULT_ERROR;
kore_log(LOG_NOTICE, "invalid type %d for validator %s",
val->type, val->name);
break;
}
return (r);
}
void
kore_validator_reload(void)
{
@ -108,3 +115,16 @@ kore_validator_reload(void)
fatal("no function for validator %s found", val->name);
}
}
struct kore_validator *
kore_validator_lookup(char *name)
{
struct kore_validator *val;
TAILQ_FOREACH(val, &validators, list) {
if (!strcmp(val->name, name))
return (val);
}
return (NULL);
}