From 5fc9ce547c4b06bf9f63e15b15a6522ff999abcf Mon Sep 17 00:00:00 2001 From: Georg Hopp Date: Tue, 20 Mar 2012 21:41:11 +0100 Subject: [PATCH] added a first basic athentication system with ldap binding. Now login depends on the existens of a valid ldap account --- assets/html/main.html | 6 ++- assets/style/common.css | 6 ++- include/auth.h | 45 +++++++++++++++++++ include/auth/ldap.h | 19 ++++++++ include/commons.h | 8 ++++ include/credential.h | 32 ++++++++++++++ include/http/message.h | 1 + include/http/parser.h | 1 + include/http/worker.h | 2 + include/interface/auth.h | 49 +++++++++++++++++++++ src/Makefile.am | 14 +++--- src/auth/ldap.c | 88 +++++++++++++++++++++++++++++++++++++ src/credential.c | 64 +++++++++++++++++++++++++++ src/http/message.c | 5 ++- src/http/parser/body.c | 2 +- src/http/parser/header.c | 6 +-- src/http/parser/post_vars.c | 4 +- src/http/worker.c | 3 ++ src/http/worker/process.c | 57 ++++++++++++++++++++---- src/http/writer/write.c | 4 +- src/interface/auth.c | 42 ++++++++++++++++++ src/interface/http_intro.c | 2 +- src/interface/subject.c | 2 +- src/webgameserver.c | 9 +++- 24 files changed, 439 insertions(+), 32 deletions(-) create mode 100644 include/auth.h create mode 100644 include/auth/ldap.h create mode 100644 include/credential.h create mode 100644 include/interface/auth.h create mode 100644 src/auth/ldap.c create mode 100644 src/credential.c create mode 100644 src/interface/auth.c diff --git a/assets/html/main.html b/assets/html/main.html index 5ea1a51..f2f3d31 100644 --- a/assets/html/main.html +++ b/assets/html/main.html @@ -21,7 +21,11 @@
- + +
+ +
+
diff --git a/assets/style/common.css b/assets/style/common.css index 9a933c6..bd3bab4 100644 --- a/assets/style/common.css +++ b/assets/style/common.css @@ -9,7 +9,11 @@ div#randval { } div#login { - position: fixed; + padding: 5px; + position: fixed; + background-color: white; + border: 1px solid black; + border-radius: 10px; } div.hide { diff --git a/include/auth.h b/include/auth.h new file mode 100644 index 0000000..a83eb51 --- /dev/null +++ b/include/auth.h @@ -0,0 +1,45 @@ +/** + * \file + * Authenticatio module factory + * + * A factory to get a specific authentication module. + * An authentication module is a class that implement the Auth interface. + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __AUTH_H__ +#define __AUTH_H__ + +#include "class.h" +#include "auth/ldap.h" + +typedef enum e_AuthModule { + AUTH_LDAP = 0 +} AuthModule; + +CLASS(Auth) { +}; + +void * authCreateById(Auth, int); +AuthLdap authCreateLdap(Auth); + +#endif // __AUTH_H__ + +// vim: set ts=4 sw=4: diff --git a/include/auth/ldap.h b/include/auth/ldap.h new file mode 100644 index 0000000..5529fd9 --- /dev/null +++ b/include/auth/ldap.h @@ -0,0 +1,19 @@ +#ifndef __AUTH_LDAP_H__ +#define __AUTH_LDAP_H__ + +#include +#include + +#include "class.h" + +CLASS(AuthLdap) { + LDAP * ldap; + char * url; + char * base_dn; + int version; + size_t nbase_dn; +}; + +#endif // __AUTH_LDAP_H__ + +// vim: set ts=4 sw=4: diff --git a/include/commons.h b/include/commons.h index d2b814f..e9642f5 100644 --- a/include/commons.h +++ b/include/commons.h @@ -5,6 +5,14 @@ #define TRUE 1 #define FALSE 0 +#ifndef MAX +# define MAX(a,b) ((a)>(b)? (a) : (b)) +#endif + +#ifndef MIN +# define MIN(a,b) ((a)<(b)? (a) : (b)) +#endif + #define SWAP_FUN(a, b) ((a)^=(b),(b)^=(a),(a)^=(b)) #define SWAP(type, a, b) do { \ diff --git a/include/credential.h b/include/credential.h new file mode 100644 index 0000000..f512b47 --- /dev/null +++ b/include/credential.h @@ -0,0 +1,32 @@ +#ifndef __CREDENTIAL_H__ +#define __CREDENTIAL_H__ + +#include + +#include "class.h" + +#define CRED_PWD(c) (((c)->cred).pwd) + +typedef enum e_CredentialType { + CRED_PASSWORD = 0 +} CredentialType; + + +CLASS(Credential) { + CredentialType type; + + union { + + struct { + char * user; + size_t nuser; + char * pass; + size_t npass; + } pwd; + + } cred; +}; + +#endif // __CREDENTIAL_H__ + +// vim: set ts=4 sw=4: diff --git a/include/http/message.h b/include/http/message.h index 5ca44f6..11b1dfa 100644 --- a/include/http/message.h +++ b/include/http/message.h @@ -38,6 +38,7 @@ CLASS(HttpMessage) { char * version; Hash header; + Hash cookies; HttpMessageType type; Stream handle; diff --git a/include/http/parser.h b/include/http/parser.h index a757aa9..cefe9db 100644 --- a/include/http/parser.h +++ b/include/http/parser.h @@ -58,6 +58,7 @@ CLASS(HttpParser) { }; ssize_t httpParserParse(void *, Stream); +void httpParserRequestVars(HttpParser); void httpParserHeader(HttpParser, const char *, const char *); void httpParserNewMessage(HttpParser, const char *, const char * lend); size_t httpParserBody(HttpParser, const char *, size_t); diff --git a/include/http/worker.h b/include/http/worker.h index 3ffc348..c62134e 100644 --- a/include/http/worker.h +++ b/include/http/worker.h @@ -52,6 +52,8 @@ CLASS(HttpWorker) { HttpWriter writer; Session session; Session * sroot; + + void * auth; }; #endif // __HTTP_WORKER_H__ diff --git a/include/interface/auth.h b/include/interface/auth.h new file mode 100644 index 0000000..ed552e7 --- /dev/null +++ b/include/interface/auth.h @@ -0,0 +1,49 @@ +/** + * \file + * The authentication interface. + * + * This is the authentication interface. It's only pupose is to + * authenticate someone or somewhat. It is called AUTH. + * The concrete access rights are managed within a class called ACL. + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __INTERFACE_AUTH_H__ +#define __INTERFACE_AUTH_H__ + +#include + +#include "interface.h" +#include "credential.h" + +typedef int (* fptr_authenticate)(void *, Credential); + +extern const struct interface i_Auth; + +struct i_Auth { + const struct interface * const _; + fptr_authenticate authenticate; +}; + +extern int authenticate(void *, Credential); + +#endif // __INTERFACE_AUTH_H__ + +// vim: set ts=4 sw=4: diff --git a/src/Makefile.am b/src/Makefile.am index c64fc3e..3abd3d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,13 +6,8 @@ IFACE = interface/class.c interface/stream_reader.c interface/logger.c \ interface/subject.c interface/observer.c interface.c SOCKET = socket.c socket/accept.c socket/connect.c socket/listen.c STREAM = stream.c stream/read.c stream/write.c -HASH = hash.c \ - hash/add.c \ - hash/get.c \ - hash/delete.c \ - hash/each.c \ - interface/hashable.c \ - hash_value.c +HASH = hash.c hash/add.c hash/get.c hash/delete.c \ + hash/each.c interface/hashable.c hash_value.c SERVER = server.c server/run.c server/close_conn.c server/poll.c \ server/handle_accept.c server/read.c server/write.c LOGGER = logger.c logger/stderr.c logger/syslog.c @@ -61,6 +56,7 @@ UTILS = utils/hash.c \ utils/http.c \ utils/daemonize.c \ utils/signalHandling.c +AUTH = interface/auth.c auth/ldap.c credential.c AM_CFLAGS = -Wall -I ../include/ @@ -70,6 +66,6 @@ bin_PROGRAMS = webgameserver webgameserver_SOURCES = webgameserver.c \ $(IFACE) $(SOCKET) $(SERVER) $(LOGGER) $(MSG) $(REQ) \ $(WRITER) $(RESP) $(HEADER) $(PARSER) $(WORKER) $(CB) \ - $(UTILS) $(MSGQ) $(SESSION) $(STREAM) $(HASH) + $(UTILS) $(MSGQ) $(SESSION) $(STREAM) $(HASH) $(AUTH) webgameserver_CFLAGS = -Wall -I ../include/ -webgameserver_LDFLAGS = -lrt -lssl +webgameserver_LDFLAGS = -lrt -lssl -lldap diff --git a/src/auth/ldap.c b/src/auth/ldap.c new file mode 100644 index 0000000..d4fd81f --- /dev/null +++ b/src/auth/ldap.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +#include "auth/ldap.h" +#include "class.h" +#include "credential.h" +#include "interface/class.h" +#include "interface/auth.h" + +#include "utils/memory.h" +#include "commons.h" + +static +int +authLdapCtor(void * _this, va_list * params) +{ + AuthLdap this = _this; + char * url = va_arg(*params, char*); + char * base_dn; + + this->url = malloc(strlen(url) + 1); + strcpy(this->url, url); + + this->version = 3; + + base_dn = va_arg(* params, char *); + this->nbase_dn = va_arg(* params, size_t); + + this->base_dn = malloc(this->nbase_dn + 1); + this->base_dn[this->nbase_dn] = 0; + memcpy(this->base_dn, base_dn, this->nbase_dn); + + return 0; +} + +static +void +authLdapDtor(void * _this) +{ + AuthLdap this = _this; + + FREE(this->base_dn); + FREE(this->url); +} + +static +int +authLdapAuthenticate(void * _this, Credential cred) +{ + AuthLdap this = _this; + char who[256]; + char * who_ptr = who; + int ldap_err; + + if (CRED_PASSWORD != cred->type) { + return FALSE; + } + + ldap_initialize(&(this->ldap), this->url); + ldap_set_option(this->ldap, LDAP_OPT_PROTOCOL_VERSION, &(this->version)); + + memcpy(who_ptr, "cn=", sizeof("cn=") - 1); + who_ptr += sizeof("cn=") - 1; + memcpy(who_ptr, CRED_PWD(cred).user, CRED_PWD(cred).nuser); + who_ptr += CRED_PWD(cred).nuser; + *who_ptr++ = ','; + memcpy(who_ptr, this->base_dn, this->nbase_dn); + who_ptr[this->nbase_dn] = 0; + + ldap_err = ldap_simple_bind_s(this->ldap, who, CRED_PWD(cred).pass); + if (0 == ldap_err) { + ldap_unbind_s(this->ldap); + //! \todo here we need to get and return the user id + return TRUE; + } + + fprintf(stderr, "%s\n", ldap_err2string(ldap_err)); + return FALSE; +} + +INIT_IFACE(Class, authLdapCtor, authLdapDtor, NULL); +INIT_IFACE(Auth, authLdapAuthenticate); +CREATE_CLASS(AuthLdap, NULL, IFACE(Class), IFACE(Auth)); + +// vim: set ts=4 sw=4: diff --git a/src/credential.c b/src/credential.c new file mode 100644 index 0000000..e2ea93d --- /dev/null +++ b/src/credential.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include "credential.h" +#include "class.h" +#include "interface/class.h" + +#include "utils/memory.h" + +static +int +credentialCtor(void * _this, va_list * params) +{ + Credential this = _this; + + this->type = va_arg(* params, CredentialType); + + switch(this->type) { + case CRED_PASSWORD: + { + char * user, *pass; + + user = va_arg(* params, char*); + CRED_PWD(this).nuser = va_arg(* params, size_t); + pass = va_arg(* params, char*); + CRED_PWD(this).npass = va_arg(* params, size_t); + + CRED_PWD(this).user = malloc(CRED_PWD(this).nuser + 1); + CRED_PWD(this).user[CRED_PWD(this).nuser] = 0; + memcpy(CRED_PWD(this).user, user, CRED_PWD(this).nuser); + + CRED_PWD(this).pass = malloc(CRED_PWD(this).npass + 1); + CRED_PWD(this).pass[CRED_PWD(this).npass] = 0; + memcpy(CRED_PWD(this).pass, pass, CRED_PWD(this).npass); + } + break; + + default: + return -1; + } + + return 0; +} + +static +void +credentialDtor(void * _this) +{ + Credential this = _this; + + switch(this->type) { + case CRED_PASSWORD: + FREE(CRED_PWD(this).user); + FREE(CRED_PWD(this).pass); + break; + } +} + +INIT_IFACE(Class, credentialCtor, credentialDtor, NULL); +CREATE_CLASS(Credential, NULL, IFACE(Class)); + +// vim: set ts=4 sw=4: diff --git a/src/http/message.c b/src/http/message.c index 747ae55..d0bb0ed 100644 --- a/src/http/message.c +++ b/src/http/message.c @@ -47,7 +47,8 @@ httpMessageCtor(void * _this, va_list * params) this->version = calloc(1, strlen(version)+1); strcpy(this->version, version); - this->header = new(Hash); + this->header = new(Hash); + this->cookies = new(Hash); return 0; } @@ -59,6 +60,8 @@ httpMessageDtor(void * _this) HttpMessage this = _this; delete(this->header); + delete(this->cookies); + FREE(this->version); switch (this->type) { diff --git a/src/http/parser/body.c b/src/http/parser/body.c index 6901c8d..310203e 100644 --- a/src/http/parser/body.c +++ b/src/http/parser/body.c @@ -28,7 +28,7 @@ #include "http/parser.h" #include "cbuf.h" -#define MIN(a,b) (((a) < (b))? (a) : (b)) +#include "commons.h" size_t httpParserBody(HttpParser this, const char * buf, size_t nbuf) diff --git a/src/http/parser/header.c b/src/http/parser/header.c index eb56010..e902c99 100644 --- a/src/http/parser/header.c +++ b/src/http/parser/header.c @@ -71,8 +71,8 @@ httpParserHeader( if (0 == strncasecmp("cookie", name, nname-1)) { HttpRequest request = (HttpRequest)this->current; - char * pair = value; - size_t togo = lend - value; + char * pair = value; + ssize_t togo = lend - value; while(NULL != pair && 0 < togo) { char * key = pair; @@ -100,8 +100,8 @@ httpParserHeader( hashAdd(request->cookies, new(HashValue, key, eqsign-key, val, nval)); - togo -= (pair - eqsign); pair++; + togo -= (pair - eqsign); } } diff --git a/src/http/parser/post_vars.c b/src/http/parser/post_vars.c index 9fe6d81..27ac86f 100644 --- a/src/http/parser/post_vars.c +++ b/src/http/parser/post_vars.c @@ -17,7 +17,7 @@ httpParserPostVars(HttpParser this) { HttpRequest request = (HttpRequest)this->current; char * pair = this->current->body; - size_t togo = this->current->nbody; + ssize_t togo = this->current->nbody; while(NULL != pair && 0 < togo) { char * key = pair; @@ -42,8 +42,8 @@ httpParserPostVars(HttpParser this) hashAdd(request->post, new(HashValue, key, eqsign-key, value, nvalue)); - togo -= (pair - eqsign); pair++; + togo -= (pair - eqsign); } } diff --git a/src/http/worker.c b/src/http/worker.c index b4dfc1a..cd96685 100644 --- a/src/http/worker.c +++ b/src/http/worker.c @@ -64,6 +64,8 @@ httpWorkerCtor(void * _this, va_list * params) this->sroot = &(this->session); + this->auth = va_arg(* params, void *); + return 0; } @@ -106,6 +108,7 @@ httpWorkerClone(void * _this, void * _base) this->writer = new(HttpWriter, base->wbuf); this->sroot = &(base->session); + this->auth = base->auth; } ssize_t httpWorkerProcess(void *, Stream); diff --git a/src/http/worker/process.c b/src/http/worker/process.c index 0b55939..43519a4 100644 --- a/src/http/worker/process.c +++ b/src/http/worker/process.c @@ -28,6 +28,7 @@ #include "class.h" #include "interface/class.h" +#include "interface/auth.h" #include "http/worker.h" #include "http/header.h" @@ -40,13 +41,17 @@ #include "stream.h" #include "hash_value.h" #include "hash.h" +#include "credential.h" #include "utils/memory.h" #include "hash.h" +#include "commons.h" + HttpMessage httpWorkerGetAsset(HttpRequest, const char *, const char *, size_t); void httpWorkerAddCommonHeader(HttpMessage, HttpMessage); + ssize_t httpWorkerProcess(HttpWorker this, Stream st) { @@ -96,16 +101,50 @@ httpWorkerProcess(HttpWorker this, Stream st) size_t nbuf; HashValue username = hashGet(request->post, CSTRA("username")); + HashValue password = hashGet(request->post, CSTRA("password")); + + /** + * \todo This is an application authorization not an HTTP + * authorization...anyway think about sending HTTP 401 + * messages if authorization is required and think about + * sending the credentials via header as described in the + * HTTP protocol. Most likely this will lead to hacky thing + * with javascript as i am not sure how far this is implemented + * within browsers. + * Anyway, for now we simply ignore a failed login within the + * response except that no session is initialized. We send + * an empty 200 OK + */ + if (NULL == password || NULL == username) { + response = new(HttpResponse, "HTTP/1.1", 200, "OK"); + } - this->session = sessionAdd( - this->sroot, - new(Session, username->value, username->nvalue)); - nbuf = sprintf(buffer, "sid=%lu;Path=/", this->session->id); - - response = (HttpMessage)httpResponseSession(this->session); - - hashAdd(response->header, - new(HttpHeader, CSTRA("Set-Cookie"), buffer, nbuf)); + if (NULL == response) { + Credential cred = new(Credential, + CRED_PASSWORD, + (char*)(username->value), username->nvalue, + (char*)(password->value), password->nvalue); + + if (!authenticate(this->auth, cred)) { + response = new(HttpResponse, "HTTP/1.1", 200, "OK"); + } else { + this->session = sessionAdd( + this->sroot, + new(Session, username->value, username->nvalue)); + nbuf = sprintf(buffer, + "sid=%lu;Path=/", + this->session->id); + + response = (HttpMessage)httpResponseSession( + this->session); + + hashAdd(response->header, + new(HttpHeader, + CSTRA("Set-Cookie"), + buffer, nbuf)); + } + delete(cred); + } } } diff --git a/src/http/writer/write.c b/src/http/writer/write.c index abf46d8..75d1dbc 100644 --- a/src/http/writer/write.c +++ b/src/http/writer/write.c @@ -30,8 +30,8 @@ #include "cbuf.h" #include "stream.h" -#define MIN(x,y) ((x) < (y) ? (x) : (y)) -#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#include "commons.h" + ssize_t httpWriterWrite(void * _this, Stream st) diff --git a/src/interface/auth.c b/src/interface/auth.c new file mode 100644 index 0000000..bc2e1bc --- /dev/null +++ b/src/interface/auth.c @@ -0,0 +1,42 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "auth.h" +#include "credential.h" +#include "interface/auth.h" + +const struct interface i_Auth = { + "auth", + 1 +}; + +int +authenticate(void * auth, Credential cred) +{ + int ret; + + RETCALL(auth, Auth, authenticate, ret, cred); + + return ret; +} + +// vim: set ts=4 sw=4: diff --git a/src/interface/http_intro.c b/src/interface/http_intro.c index 2f12eb0..bf14c4d 100644 --- a/src/interface/http_intro.c +++ b/src/interface/http_intro.c @@ -4,7 +4,7 @@ * \author Georg Hopp * * \copyright - * Copyright (C) 2012 Georg Hopp + * Copyright © 2012 Georg Hopp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/interface/subject.c b/src/interface/subject.c index 4ee3f4a..831553e 100644 --- a/src/interface/subject.c +++ b/src/interface/subject.c @@ -4,7 +4,7 @@ * \author Georg Hopp * * \copyright - * Copyright (C) 2012 Georg Hopp + * Copyright © 2012 Georg Hopp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/webgameserver.c b/src/webgameserver.c index 9764cec..853504e 100644 --- a/src/webgameserver.c +++ b/src/webgameserver.c @@ -38,11 +38,13 @@ #include "server.h" #include "logger.h" #include "http/worker.h" +#include "auth/ldap.h" #include "interface/class.h" #include "interface/logger.h" #include "utils/signalHandling.h" +#include "utils/memory.h" #define DEFAULT_SECS 10 //#define DEFAULT_USECS (1000000 / HZ * 2) @@ -126,6 +128,7 @@ main() default: { Logger logger; + AuthLdap auth; HttpWorker worker; Server server; @@ -136,7 +139,10 @@ main() close(shm); logger = new(LoggerSyslog, LOGGER_ERR); - worker = new(HttpWorker, "testserver", value); + auth = new(AuthLdap, + "ldap://localhost/", + CSTRA("ou=user,dc=yabrog,dc=weird-web-workers,dc=org")); + worker = new(HttpWorker, "testserver", value, auth); server = new(Server, logger, worker, 11212, SOMAXCONN); //daemonize(); @@ -184,6 +190,7 @@ main() if (NULL != server) delete(server); if (NULL != worker) delete(worker); + if (NULL != auth) delete(auth); if (NULL != logger) delete(logger); }