Add authentication blocks for Kore.

Using authentication blocks one can define "authentication" mechanisms
in Kore for page handlers.

This can be used to require a session cookie (validated by your own validator)
for certain page handlers, and hopefully in the future provide a framework
for adding more authentication things (like HTTP Auth).

Right now only cookie checking is available.
This commit is contained in:
Joris Vink 2014-01-22 22:55:10 +01:00
parent 7e8371366f
commit 9a8092bf41
12 changed files with 463 additions and 18 deletions

View File

@ -3,9 +3,10 @@
CC=gcc
BIN=kore
S_SRC+= src/kore.c src/accesslog.c src/buf.c src/config.c src/connection.c \
src/domain.c src/http.c src/mem.c src/module.c src/net.c src/pool.c \
src/spdy.c src/validator.c src/utils.c src/worker.c src/zlib_dict.c
S_SRC+= src/kore.c src/accesslog.c src/auth.c src/buf.c src/config.c \
src/connection.c src/domain.c src/http.c src/mem.c src/module.c \
src/net.c src/pool.c src/spdy.c src/validator.c src/utils.c \
src/worker.c src/zlib_dict.c
S_OBJS= $(S_SRC:.c=.o)
CFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes

View File

@ -25,6 +25,7 @@
#define HTTP_USERAGENT_LEN 256
#define HTTP_REQ_HEADER_MAX 25
#define HTTP_MAX_QUERY_ARGS 10
#define HTTP_MAX_COOKIES 10
#define HTTP_ARG_TYPE_RAW 0
#define HTTP_ARG_TYPE_BYTE 1
@ -133,8 +134,10 @@ struct http_request {
void *hdlr_extra;
char *query_string;
u_int8_t *multipart_body;
struct kore_module_handle *hdlr;
TAILQ_HEAD(, http_header) req_headers;
TAILQ_HEAD(, http_header) resp_headers;
TAILQ_HEAD(, http_arg) arguments;

View File

@ -171,12 +171,24 @@ struct kore_handler_params {
TAILQ_ENTRY(kore_handler_params) list;
};
#define HANDLER_TYPE_STATIC 1
#define HANDLER_TYPE_DYNAMIC 2
#define KORE_AUTH_TYPE_COOKIE 1
struct kore_auth {
u_int8_t type;
char *name;
char *value;
char *redirect;
struct kore_validator *validator;
TAILQ_ENTRY(kore_auth) list;
};
#define KORE_MODULE_LOAD 1
#define KORE_MODULE_UNLOAD 2
#define HANDLER_TYPE_STATIC 1
#define HANDLER_TYPE_DYNAMIC 2
struct kore_module {
void *handle;
char *path;
@ -195,6 +207,7 @@ struct kore_module_handle {
int type;
int errors;
regex_t rctx;
struct kore_auth *auth;
TAILQ_HEAD(, kore_handler_params) params;
TAILQ_ENTRY(kore_module_handle) list;
@ -329,6 +342,13 @@ void kore_accesslog_init(void);
int kore_accesslog_wait(void);
void kore_accesslog_worker_init(void);
int kore_auth(struct http_request *, struct kore_auth *);
int kore_auth_cookie(struct http_request *, struct kore_auth *);
void kore_auth_init(void);
int kore_auth_new(char *);
struct kore_auth *kore_auth_lookup(char *);
int kore_ssl_sni_cb(SSL *, int *, void *);
int kore_server_bind(const char *, const char *);
int kore_ssl_npn_cb(SSL *, const u_char **, unsigned int *, void *);
@ -380,7 +400,7 @@ void kore_domain_closelogs(void);
void *kore_module_getsym(char *);
void kore_module_load(char *, char *);
void kore_domain_sslstart(struct kore_domain *);
int kore_module_handler_new(char *, char *, char *, int);
int kore_module_handler_new(char *, char *, char *, char *, int);
struct kore_domain *kore_domain_lookup(const char *);
struct kore_module_handle *kore_module_handler_find(char *, char *);

View File

@ -0,0 +1,16 @@
<!DOCTYPE>
<html>
<head>
<link rel="stylesheet" href="/css/style.css" type="text/css">
<title>Kore Authentication tests</title>
</head>
<body>
<div class="content">
<p style="font-size: small">The cookie session_id should now be set.</p>
<p style="font-size: small">You can continue to <a href="/private/test">view page handler in auth block</a></p>
</div>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE>
<html>
<head>
<link rel="stylesheet" href="/css/style.css" type="text/css">
<title>Kore Authentication tests</title>
</head>
<body>
<div class="content">
<p style="font-size: small">If you see this, the authentication worked. This page should redirect back to /private once you remove your session_id cookie.</p>
</div>
</body>
</html>

View File

@ -58,6 +58,7 @@ load modules/example/example.module example_load
validator v_example function v_example_func
validator v_regex regex ^/test/[a-z]*$
validator v_number regex ^[0-9]*$
validator v_session function v_session_validate
# Specify the SSL ciphers that will be used.
#ssl_cipher ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK
@ -73,6 +74,38 @@ ssl_no_compression
# You can keep it open indefinately by setting this to 0.
#spdy_idle_time 120
# Authentication configuration
#
# Using authentication blocks you can define a standard way for
# Kore to validate your users. In the example below we create
# a authentication block called auth_example, which requires
# a cookie (session_id) to be set.
#
# If no cookie is present or the cookie is not valid according
# to the set validator, Kore will redirect the browser to the
# URI set in authentication_uri.
#
# Page handlers can be bound to authentication by specifying
# authentication block at the end of the page directive (see below).
authentication auth_example {
# The authentication type denotes the way the user should
# be authenticated. Right now only cookie is available.
authentication_type cookie
# The name of the cookie to look for.
authentication_value session_id
# The validator that will be called to verify the cookie.
# Note this is YOUR validator, Kore does not have built-in
# session support. You must add this manually using your
# preferred method (Storing it in postgres, redis, ...)
authentication_validator v_session
# The URI Kore will redirect to if a authentication fails.
# If this is not set, Kore will return a simple 403.
authentication_uri /private
}
# Domain configuration
#
# Each domain configuration starts with listing what domain
@ -92,7 +125,11 @@ ssl_no_compression
# Dynamic handlers take a POSIX regular expression as its path.
#
# Syntax:
# handler path module_callback
# handler path module_callback [auth block]
#
# Note that the auth block is optional and if set will force Kore to
# authenticate the user according to the authentication block its settings
# before allowing access to the page.
# Example domain that responds to localhost.
domain localhost {
@ -100,7 +137,7 @@ domain localhost {
certkey cert/server.key
accesslog /var/log/kore_access.log
# Static page handlers
# Page handlers with no authentication required.
static /css/style.css serve_style_css
static / serve_index
static /intro.jpg serve_intro
@ -109,8 +146,11 @@ domain localhost {
static /upload serve_file_upload
static /lock-test serve_lock_test
static /validator serve_validator
static /params-test serve_params_test
static /private serve_private
# Page handlers with authentication.
static /private/test serve_private_test auth_example
# Configure /params-test POST to only accept the following parameters.
# They are automatically tested against the validator listed.

View File

@ -20,6 +20,7 @@
#include "static.h"
void example_load(int);
int serve_style_css(struct http_request *);
int serve_index(struct http_request *);
int serve_intro(struct http_request *);
@ -29,9 +30,12 @@ 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 *);
int serve_private(struct http_request *);
int serve_private_test(struct http_request *);
void my_callback(void);
int v_example_func(char *);
int v_session_validate(char *);
void test_base64(u_int8_t *, u_int32_t, struct kore_buf *);
char *b64tests[] = {
@ -309,6 +313,33 @@ serve_params_test(struct http_request *req)
return (r);
}
int
serve_private(struct http_request *req)
{
int r;
http_response_header_add(req, "content-type", "text/html");
http_response_header_add(req, "set-cookie", "session_id=test123");
r = http_response(req, 200, static_html_private,
static_len_html_private);
return (r);
}
int
serve_private_test(struct http_request *req)
{
int r;
http_response_header_add(req, "content-type", "text/html");
r = http_response(req, 200, static_html_private_test,
static_len_html_private_test);
return (r);
}
void
my_callback(void)
{
@ -328,3 +359,14 @@ v_example_func(char *data)
return (KORE_RESULT_ERROR);
}
int
v_session_validate(char *data)
{
kore_log(LOG_NOTICE, "v_session_validate: %s", data);
if (!strcmp(data, "test123"))
return (KORE_RESULT_OK);
return (KORE_RESULT_ERROR);
}

135
src/auth.c Normal file
View File

@ -0,0 +1,135 @@
/*
* Copyright (c) 2014 Joris Vink <joris@coders.se>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <ctype.h>
#include "kore.h"
#include "http.h"
TAILQ_HEAD(, kore_auth) auth_list;
void
kore_auth_init(void)
{
TAILQ_INIT(&auth_list);
}
int
kore_auth_new(char *name)
{
struct kore_auth *auth;
if ((auth = kore_auth_lookup(name)) != NULL)
return (KORE_RESULT_ERROR);
auth = kore_malloc(sizeof(*auth));
auth->type = 0;
auth->value = NULL;
auth->redirect = NULL;
auth->validator = NULL;
auth->name = kore_strdup(name);
TAILQ_INSERT_TAIL(&auth_list, auth, list);
return (KORE_RESULT_OK);
}
int
kore_auth(struct http_request *req, struct kore_auth *auth)
{
int r;
kore_debug("kore_auth(%p, %p)", req, auth);
switch (auth->type) {
case KORE_AUTH_TYPE_COOKIE:
r = kore_auth_cookie(req, auth);
break;
default:
kore_log(LOG_NOTICE, "unknown auth type %d", auth->type);
return (KORE_RESULT_ERROR);
}
if (r == KORE_RESULT_OK) {
kore_debug("kore_auth() for %s successful", req->path);
return (KORE_RESULT_OK);
}
kore_debug("kore_auth() for %s failed", req->path);
if (auth->redirect == NULL) {
http_response(req, 403, NULL, 0);
return (KORE_RESULT_ERROR);
}
http_response_header_add(req, "location", auth->redirect);
http_response(req, 302, NULL, 0);
return (KORE_RESULT_ERROR);
}
int
kore_auth_cookie(struct http_request *req, struct kore_auth *auth)
{
int i, v;
size_t len, slen;
char *value, *c, *cookie, *cookies[HTTP_MAX_COOKIES];
if (!http_request_header_get(req, "cookie", &cookie))
return (KORE_RESULT_ERROR);
slen = strlen(auth->value);
v = kore_split_string(cookie, ";", cookies, HTTP_MAX_COOKIES);
for (i = 0; i < v; i++) {
for (c = cookies[i]; isspace(*c); c++)
;
len = MIN(slen, strlen(cookies[i]));
if (!strncmp(c, auth->value, len))
break;
}
if (i == v) {
kore_mem_free(cookie);
return (KORE_RESULT_ERROR);
}
c = cookies[i];
if ((value = strchr(c, '=')) == NULL) {
kore_mem_free(cookie);
return (KORE_RESULT_ERROR);
}
i = kore_validator_check(auth->validator, ++value);
kore_mem_free(cookie);
return (i);
}
struct kore_auth *
kore_auth_lookup(char *name)
{
struct kore_auth *auth;
TAILQ_FOREACH(auth, &auth_list, list) {
if (!strcmp(auth->name, name))
return (auth);
}
return (NULL);
}

View File

@ -22,6 +22,8 @@
#include "kore.h"
#include "http.h"
/* XXX - This is becoming a clusterfuck. Fix it. */
static int configure_bind(char **);
static int configure_load(char **);
static int configure_handler(char **);
@ -49,6 +51,12 @@ static int configure_validator(char **);
static int configure_params(char **);
static int configure_validate(char **);
static int configure_require_client_cert(char **);
static int configure_authentication(char **);
static int configure_authentication_uri(char **);
static int configure_authentication_type(char **);
static int configure_authentication_value(char **);
static int configure_authentication_validator(char **);
static void domain_sslstart(void);
static struct {
@ -83,11 +91,17 @@ static struct {
{ "validator", configure_validator },
{ "params", configure_params },
{ "validate", configure_validate },
{ "authentication", configure_authentication },
{ "authentication_uri", configure_authentication_uri },
{ "authentication_type", configure_authentication_type },
{ "authentication_value", configure_authentication_value },
{ "authentication_validator", configure_authentication_validator },
{ NULL, NULL },
};
char *config_file = NULL;
static u_int8_t current_method = 0;
static struct kore_auth *current_auth = NULL;
static struct kore_domain *current_domain = NULL;
static struct kore_module_handle *current_handler = NULL;
@ -127,6 +141,17 @@ kore_parse_config(void)
continue;
}
if (!strcmp(p, "}") && current_auth != NULL) {
if (current_auth->validator == NULL) {
fatal("no authentication validator for %s",
current_auth->name);
}
lineno++;
current_auth = NULL;
continue;
}
if (!strcmp(p, "}") && current_domain != NULL)
domain_sslstart();
@ -296,7 +321,7 @@ configure_handler(char **argv)
return (KORE_RESULT_ERROR);
if (!kore_module_handler_new(argv[1],
current_domain->domain, argv[2], type)) {
current_domain->domain, argv[2], argv[3], type)) {
kore_debug("cannot create handler for %s", argv[1]);
return (KORE_RESULT_ERROR);
}
@ -723,6 +748,130 @@ configure_validate(char **argv)
return (KORE_RESULT_OK);
}
static int
configure_authentication(char **argv)
{
if (argv[2] == NULL) {
printf("Missing name for authentication block\n");
return (KORE_RESULT_ERROR);
}
if (current_auth != NULL) {
printf("Previous authentication block not closed\n");
return (KORE_RESULT_ERROR);
}
if (strcmp(argv[2], "{")) {
printf("missing { for authentication block\n");
return (KORE_RESULT_ERROR);
}
if (!kore_auth_new(argv[1]))
return (KORE_RESULT_ERROR);
current_auth = kore_auth_lookup(argv[1]);
return (KORE_RESULT_OK);
}
static int
configure_authentication_type(char **argv)
{
if (current_auth == NULL) {
printf("authentication_type outside authentication block\n");
return (KORE_RESULT_ERROR);
}
if (argv[1] == NULL) {
printf("missing parameter for authentication_type\n");
return (KORE_RESULT_ERROR);
}
if (!strcmp(argv[1], "cookie")) {
current_auth->type = KORE_AUTH_TYPE_COOKIE;
} else {
printf("unknown authentication type '%s'\n", argv[1]);
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
static int
configure_authentication_value(char **argv)
{
if (current_auth == NULL) {
printf("authentication_value outside authentication block\n");
return (KORE_RESULT_ERROR);
}
if (argv[1] == NULL) {
printf("missing parameter for authentication_value\n");
return (KORE_RESULT_ERROR);
}
if (current_auth->value != NULL) {
printf("duplicate authentication_value found\n");
return (KORE_RESULT_ERROR);
}
current_auth->value = kore_strdup(argv[1]);
return (KORE_RESULT_OK);
}
static int
configure_authentication_validator(char **argv)
{
struct kore_validator *val;
if (current_auth == NULL) {
printf("authentication_validator outside authentication\n");
return (KORE_RESULT_ERROR);
}
if (argv[1] == NULL) {
printf("missing parameter for authentication_validator\n");
return (KORE_RESULT_ERROR);
}
if (current_auth->validator != NULL) {
printf("duplicate authentication_validator found\n");
return (KORE_RESULT_ERROR);
}
if ((val = kore_validator_lookup(argv[1])) == NULL) {
printf("authentication validator '%s' not found\n", argv[1]);
return (KORE_RESULT_ERROR);
}
current_auth->validator = val;
return (KORE_RESULT_OK);
}
static int
configure_authentication_uri(char **argv)
{
if (current_auth == NULL) {
printf("authentication_uri outside authentication block\n");
return (KORE_RESULT_ERROR);
}
if (argv[1] == NULL) {
printf("missing parameter for authentication_uri\n");
return (KORE_RESULT_ERROR);
}
if (current_auth->redirect != NULL) {
printf("duplicate authentication_uri found\n");
return (KORE_RESULT_ERROR);
}
current_auth->redirect = kore_strdup(argv[1]);
return (KORE_RESULT_OK);
}
static void
domain_sslstart(void)
{

View File

@ -150,11 +150,24 @@ http_process(void)
if (hdlr == NULL) {
r = http_generic_404(req);
} else {
req->hdlr = hdlr;
cb = hdlr->addr;
worker->active_hdlr = hdlr;
r = cb(req);
worker->active_hdlr = NULL;
if (hdlr->auth != NULL)
r = kore_auth(req, hdlr->auth);
else
r = KORE_RESULT_OK;
if (r == KORE_RESULT_OK) {
req->hdlr = hdlr;
cb = hdlr->addr;
worker->active_hdlr = hdlr;
r = cb(req);
worker->active_hdlr = NULL;
} else {
/*
* Set r to KORE_RESULT_OK so we can properly
* flush the result from kore_auth().
*/
r = KORE_RESULT_OK;
}
}
req->end = kore_time_ms();

View File

@ -87,6 +87,7 @@ main(int argc, char *argv[])
kore_log_init();
kore_mem_init();
kore_auth_init();
kore_domain_init();
kore_module_init();
kore_validator_init();

View File

@ -136,14 +136,16 @@ kore_module_loaded(void)
}
int
kore_module_handler_new(char *path, char *domain, char *func, int type)
kore_module_handler_new(char *path, char *domain, char *func,
char *auth, int type)
{
struct kore_auth *ap;
void *addr;
struct kore_domain *dom;
struct kore_module_handle *hdlr;
kore_debug("kore_module_handler_new(%s, %s, %s, %d)", path,
domain, func, type);
kore_debug("kore_module_handler_new(%s, %s, %s, %s, %d)", path,
domain, func, auth, type);
addr = kore_module_getsym(func);
if (addr == NULL) {
@ -154,7 +156,15 @@ kore_module_handler_new(char *path, char *domain, char *func, int type)
if ((dom = kore_domain_lookup(domain)) == NULL)
return (KORE_RESULT_ERROR);
if (auth != NULL) {
if ((ap = kore_auth_lookup(auth)) == NULL)
fatal("no authentication block '%s' found", auth);
} else {
ap = NULL;
}
hdlr = kore_malloc(sizeof(*hdlr));
hdlr->auth = ap;
hdlr->errors = 0;
hdlr->addr = addr;
hdlr->type = type;