mirror of https://git.kore.io/kore.git
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:
parent
34c2f31a93
commit
1e250c1473
|
@ -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;
|
||||
|
|
|
@ -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 *, ...);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
88
src/config.c
88
src/config.c
|
@ -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)
|
||||
{
|
||||
|
|
65
src/http.c
65
src/http.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue