diff --git a/config.h.in b/config.h.in index 983de0fa..e51ed76a 100644 --- a/config.h.in +++ b/config.h.in @@ -111,6 +111,9 @@ /* Define to 1 if you have the `fstat' function. */ #undef HAVE_FSTAT +/* Enable the GDBM library */ +#undef HAVE_GDBM + /* Define to 1 if you have the `getdtablesize' function. */ #undef HAVE_GETDTABLESIZE diff --git a/configure.ac b/configure.ac index 12d77f3a..38ec2bc0 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,25 @@ AC_C_BIGENDIAN PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= 3.0.28]) +gdbm_enabled=no +LIBS="$oldlibs -lgdbm" +AC_MSG_CHECKING([for gdbm library]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([ + #include ],[ + GDBM_FILE dbf; + dbf = gdbm_open("",0,0,0,0);])], + [AC_MSG_RESULT(yes) + AC_SUBST([GDBM_LIBS], [-lgdbm]) + AC_SUBST([GDBM_CFLAGS], []) + gdbm_enabled=yes + AC_DEFINE([HAVE_GDBM], 1, [Enable the GDBM library])], + [AC_MSG_RESULT(no) + AC_MSG_WARN([[ +*** +*** gdbm was not found. The server will not be able to maintain state across restarts. +*** ]])]) +LIBS="$oldlibs" + LIBS="$oldlibs -lpam" AC_MSG_CHECKING([for pam library]) AC_LINK_IFELSE([AC_LANG_PROGRAM([ @@ -56,5 +75,6 @@ AC_MSG_NOTICE([summary of build options: Install prefix: ${prefix} Compiler: ${CC} CFlags: ${CFLAGS} + GDBM backend: ${gdbm_enabled} ]) diff --git a/src/Makefile.am b/src/Makefile.am index f46776be..8a482b91 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,10 +14,10 @@ CCAN_SOURCES = ccan/build_assert/build_assert.h ccan/check_type/check_type.h \ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ - cookies.c http-parser/http_parser.c ipc.h \ + http-parser/http_parser.c ipc.h cookies.c \ vpn.h cookies.h tlslib.h http-parser/http_parser.h log.c tun.c tun.h \ config.c pam.c pam.h worker-resume.c worker.h main-resume.c main.h \ - main-user.c $(CCAN_SOURCES) + main-user.c cookies-gdbm.c cookies-hash.c $(CCAN_SOURCES) ocserv_SOURCES += ocserv-args.def ocserv-args.c ocserv-args.h diff --git a/src/config.c b/src/config.c index 8fc38daa..adeb1bb7 100644 --- a/src/config.c +++ b/src/config.c @@ -143,6 +143,8 @@ unsigned j; READ_STRING("tls-priorities", config->priorities, 0); READ_STRING("chroot-dir", config->chroot_dir, 0); + READ_STRING("cookie-db", config->cookie_db_name, 0); + READ_NUMERIC("cookie-validity", config->cookie_validity, 1); READ_NUMERIC("auth-timeout", config->auth_timeout, 0); READ_NUMERIC("max-clients", config->max_clients, 0); diff --git a/src/cookies-gdbm.c b/src/cookies-gdbm.c new file mode 100644 index 00000000..5db3c971 --- /dev/null +++ b/src/cookies-gdbm.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_GDBM + +static +int store_cookie_gdbm(main_server_st *s, const struct stored_cookie_st* sc) +{ +GDBM_FILE dbf; +datum key; +datum data; +int ret; + + dbf = gdbm_open((char*)s->config->cookie_db_name, 0, GDBM_WRCREAT, S_IRUSR|S_IWUSR, NULL); + if (dbf == NULL) { + syslog(LOG_ERR, "Cannot open cookie database: %s", s->config->cookie_db_name); + return -1; + } + + key.dptr = (void*)sc->cookie; + key.dsize = sizeof(sc->cookie); + data.dptr = (void*)sc; + data.dsize = sizeof(*sc); + + ret = gdbm_store( dbf, key, data, GDBM_INSERT); + if (ret != 0) { + ret = -1; + goto finish; + } + + ret = 0; + +finish: + gdbm_close(dbf); + return ret; +} + +static +int retrieve_cookie_gdbm(main_server_st *s, const void* cookie, unsigned cookie_size, + struct stored_cookie_st* sc) +{ +GDBM_FILE dbf; +datum key; +datum data; +int ret; + + dbf = gdbm_open((char*)s->config->cookie_db_name, 0, GDBM_READER, 0, NULL); + if (dbf == NULL) { + syslog(LOG_ERR, "Cannot open cookie database: %s", s->config->cookie_db_name); + return -1; + } + + key.dptr = (void*)cookie; + key.dsize = cookie_size; + + data = gdbm_fetch( dbf, key); + if (data.dsize != sizeof(*sc)) { + ret = -1; + goto finish; + } + memcpy(sc, data.dptr, data.dsize); + + if (sc->expiration >= time(0)) + ret = 0; + else + ret = -1; + +finish: + gdbm_close(dbf); + return ret; +} + +static +void expire_cookies_gdbm(main_server_st* s) +{ +GDBM_FILE dbf; +datum key; +datum data; +int deleted = 0; +struct stored_cookie_st sc; +time_t now = time(0); + + dbf = gdbm_open((char*)s->config->cookie_db_name, 0, GDBM_WRITER, 0, NULL); + if (dbf == NULL) + return; + + key = gdbm_firstkey(dbf); + if (key.dptr == NULL) + goto finish; + + while(key.dptr != NULL) { + data = gdbm_fetch( dbf, key); + if (data.dsize != sizeof(sc)) { + gdbm_delete(dbf, key); + deleted++; + } else { + memcpy(&sc, data.dptr, data.dsize); + if (sc.expiration <= now) { + gdbm_delete(dbf, key); + deleted++; + } + } + + key = gdbm_nextkey(dbf, key); + } + + if (deleted > 0) + gdbm_reorganize(dbf); + +finish: + gdbm_close(dbf); +} + +static +int cookie_db_init_gdbm(main_server_st* s) +{ + return 0; +} + +static +void cookie_db_deinit_gdbm(main_server_st* s) +{ + return; +} + +struct cookie_storage_st gdbm_cookie_funcs = { + .store = store_cookie_gdbm, + .retrieve = retrieve_cookie_gdbm, + .expire = expire_cookies_gdbm, + .init = cookie_db_init_gdbm, + .deinit = cookie_db_deinit_gdbm, +}; + +#endif diff --git a/src/cookies-hash.c b/src/cookies-hash.c new file mode 100644 index 00000000..e4c942c6 --- /dev/null +++ b/src/cookies-hash.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_COOKIES(n) ((n>0)?(2*n):4096) + +/* receives allocated data and stores them. + */ +int store_cookie_hash(main_server_st *s, const struct stored_cookie_st* sc) +{ +size_t key; + + if (s->cookie_db->entries >= MAX_COOKIES(s->config->max_clients)) { + syslog(LOG_INFO, "Maximum number of cookies was reached (%u)", MAX_COOKIES(s->config->max_clients)); + return -1; + } + + key = hash_stable_8(sc->cookie, COOKIE_SIZE, 0); + + htable_add(&s->cookie_db->ht, key, sc); + s->cookie_db->entries++; + + return 0; +} + +int retrieve_cookie_hash(main_server_st *s, const void* cookie, unsigned cookie_size, + struct stored_cookie_st* rsc) +{ +size_t key; +struct htable_iter iter; +struct stored_cookie_st * sc; + + key = hash_stable_8(cookie, cookie_size, 0); + + sc = htable_firstval(&s->cookie_db->ht, &iter, key); + while(sc != NULL) { + if (cookie_size == COOKIE_SIZE && + memcmp (cookie, sc->cookie, COOKIE_SIZE) == 0) { + + if (sc->expiration < time(0)) + return -1; + + memcpy(rsc, sc, sizeof(*sc)); + return 0; + } + + sc = htable_nextval(&s->cookie_db->ht, &iter, key); + } + + return -1; +} + +void expire_cookies_hash(main_server_st* s) +{ +struct stored_cookie_st *sc; +struct htable_iter iter; +time_t now = time(0); + + sc = htable_first(&s->cookie_db->ht, &iter); + while(sc != NULL) { + if (sc->expiration <= now) { + htable_delval(&s->cookie_db->ht, &iter); + free(sc); + s->cookie_db->entries--; + } + sc = htable_next(&s->cookie_db->ht, &iter); + } +} + +static size_t rehash(const void *_e, void *unused) +{ +const struct stored_cookie_st *e = _e; + + return hash_stable_8(e->cookie, COOKIE_SIZE, 0); +} + +int cookie_db_init_hash(main_server_st * s) +{ +hash_db_st * db; + + db = malloc(sizeof(*db)); + if (db == NULL) + return -1; + + htable_init(&db->ht, rehash, NULL); + db->entries = 0; + + s->cookie_db = db; + + return 0; +} + +void cookie_db_deinit_hash(main_server_st* s) +{ +struct stored_cookie_st* cache; +struct htable_iter iter; + + cache = htable_first(&s->cookie_db->ht, &iter); + while(cache != NULL) { + free(cache); + cache = htable_next(&s->cookie_db->ht, &iter); + } + htable_clear(&s->cookie_db->ht); + s->cookie_db->entries = 0; + + return; +} + +struct cookie_storage_st hash_cookie_funcs = { + .store = store_cookie_hash, + .retrieve = retrieve_cookie_hash, + .expire = expire_cookies_hash, + .init = cookie_db_init_hash, + .deinit = cookie_db_deinit_hash, +}; diff --git a/src/cookies.c b/src/cookies.c index 49d80dce..cb6e905c 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -30,111 +30,32 @@ #include #include #include +#include #include #include #include -#include -#include -#define MAX_COOKIES(n) ((n>0)?(2*n):4096) +cookie_store_fn store_cookie; +cookie_retrieve_fn retrieve_cookie; +cookie_db_deinit_fn cookie_db_deinit; +cookie_expire_fn expire_cookies; -/* receives allocated data and stores them. - */ -int store_cookie(main_server_st *s, struct stored_cookie_st* sc) +int cookie_db_init(main_server_st* s) { -size_t key; +struct cookie_storage_st* funcs; - if (s->cookie_db->entries >= MAX_COOKIES(s->config->max_clients)) { - syslog(LOG_INFO, "Maximum number of cookies was reached (%u)", MAX_COOKIES(s->config->max_clients)); - return -1; - } +#ifdef HAVE_GDBM + if (s->config->cookie_db_name != NULL) + funcs = &gdbm_cookie_funcs; + else +#endif + funcs = &hash_cookie_funcs; - key = hash_stable_8(sc->cookie, COOKIE_SIZE, 0); + cookie_db_deinit = funcs->deinit; + expire_cookies = funcs->expire; + store_cookie = funcs->store; + retrieve_cookie = funcs->retrieve; - htable_add(&s->cookie_db->ht, key, sc); - s->cookie_db->entries++; - - return 0; -} - -int retrieve_cookie(main_server_st *s, const void* cookie, unsigned cookie_size, - struct stored_cookie_st* rsc) -{ -size_t key; -struct htable_iter iter; -struct stored_cookie_st * sc; - - key = hash_stable_8(cookie, cookie_size, 0); - - sc = htable_firstval(&s->cookie_db->ht, &iter, key); - while(sc != NULL) { - if (cookie_size == COOKIE_SIZE && - memcmp (cookie, sc->cookie, COOKIE_SIZE) == 0) { - - if (sc->expiration < time(0)) - return -1; - - memcpy(rsc, sc, sizeof(*sc)); - return 0; - } - - sc = htable_nextval(&s->cookie_db->ht, &iter, key); - } - - return -1; -} - -void expire_cookies(main_server_st* s) -{ -struct stored_cookie_st *sc; -struct htable_iter iter; -time_t now = time(0); - - sc = htable_first(&s->cookie_db->ht, &iter); - while(sc != NULL) { - if (sc->expiration <= now) { - htable_delval(&s->cookie_db->ht, &iter); - free(sc); - s->cookie_db->entries--; - } - sc = htable_next(&s->cookie_db->ht, &iter); - } -} - -static size_t rehash(const void *_e, void *unused) -{ -const struct stored_cookie_st *e = _e; - - return hash_stable_8(e->cookie, COOKIE_SIZE, 0); -} - -void cookie_db_init(hash_db_st** _db) -{ -hash_db_st * db; - - db = malloc(sizeof(*db)); - if (db == NULL) - exit(1); - - htable_init(&db->ht, rehash, NULL); - db->entries = 0; - - *_db = db; -} - -void cookie_db_deinit(hash_db_st* db) -{ -struct stored_cookie_st* cache; -struct htable_iter iter; - - cache = htable_first(&db->ht, &iter); - while(cache != NULL) { - free(cache); - cache = htable_next(&db->ht, &iter); - } - htable_clear(&db->ht); - db->entries = 0; - - return; + return funcs->init(s); } diff --git a/src/cookies.h b/src/cookies.h index c8a508c9..26f2126b 100644 --- a/src/cookies.h +++ b/src/cookies.h @@ -12,12 +12,31 @@ struct stored_cookie_st { time_t expiration; }; -int store_cookie(main_server_st *, struct stored_cookie_st* sc); +typedef int (*cookie_store_fn)(main_server_st *, const struct stored_cookie_st* sc); -int retrieve_cookie(main_server_st *, const void* cookie, unsigned cookie_size, +typedef int (*cookie_retrieve_fn)(main_server_st *, const void* cookie, unsigned cookie_size, struct stored_cookie_st* sc); -void cookie_db_deinit(hash_db_st* db); -void cookie_db_init(hash_db_st** _db); +typedef void (*cookie_db_deinit_fn)(main_server_st*); +typedef void (*cookie_expire_fn)(main_server_st* s); + +extern cookie_store_fn store_cookie; +extern cookie_retrieve_fn retrieve_cookie; +extern cookie_db_deinit_fn cookie_db_deinit; +extern cookie_expire_fn expire_cookies; + +int cookie_db_init(main_server_st*); + + +struct cookie_storage_st { + cookie_store_fn store; + cookie_retrieve_fn retrieve; + cookie_expire_fn expire; + int (*init)(main_server_st *); + cookie_db_deinit_fn deinit; +}; + +extern struct cookie_storage_st gdbm_cookie_funcs; +extern struct cookie_storage_st hash_cookie_funcs; #endif diff --git a/src/main.c b/src/main.c index e8006293..d9efbe45 100644 --- a/src/main.c +++ b/src/main.c @@ -540,7 +540,6 @@ int main(int argc, char** argv) list_head_init(&clist.head); tun_st_init(&tun); tls_cache_init(&s.tls_db); - cookie_db_init(&s.cookie_db); signal(SIGINT, handle_term); signal(SIGTERM, handle_term); @@ -560,11 +559,17 @@ int main(int argc, char** argv) fprintf(stderr, "This server requires root access to operate.\n"); exit(1); } - + s.config = &config; s.tun = &tun; s.llist = &llist; s.clist = &clist; + + ret = cookie_db_init(&s); + if (ret < 0) { + fprintf(stderr, "Could not initialize cookie database.\n"); + exit(1); + } /* Listen to network ports */ ret = listen_ports(&config, &llist, config.name); diff --git a/src/main.h b/src/main.h index 933a4da3..4be763f5 100644 --- a/src/main.h +++ b/src/main.h @@ -84,8 +84,6 @@ int handle_resume_fetch_req(main_server_st* s, struct proc_st* proc, int handle_resume_store_req(main_server_st* s, struct proc_st *proc, const struct cmd_resume_store_req_st * req); -void expire_cookies(main_server_st* s); - void __attribute__ ((format(printf, 4, 5))) mslog(const main_server_st * s, const struct proc_st* proc, diff --git a/src/ocserv-args.c b/src/ocserv-args.c index 397da1d1..39232d0e 100644 --- a/src/ocserv-args.c +++ b/src/ocserv-args.c @@ -2,7 +2,7 @@ * * DO NOT EDIT THIS FILE (ocserv-args.c) * - * It has been AutoGen-ed February 7, 2013 at 02:45:14 PM by AutoGen 5.16 + * It has been AutoGen-ed February 7, 2013 at 08:21:00 PM by AutoGen 5.16 * From the definitions ocserv-args.def * and the template file options * diff --git a/src/ocserv-args.def b/src/ocserv-args.def index 960b4054..258267d0 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -110,6 +110,10 @@ auth-timeout = 40 # of that cookie. cookie-validity = 14400 +# A cookie database. If not set cookies are stored in memory and +# server restarts won't preserve them. +#cookie-db = /var/tmp/cookies.db + # Script to call when a client connects and obtains an IP # Parameters: username hostname device IP-REAL IP-LOCAL IP-REMOTE # hostname is the hostname selected by the client diff --git a/src/ocserv-args.h b/src/ocserv-args.h index af9bc648..a8083d52 100644 --- a/src/ocserv-args.h +++ b/src/ocserv-args.h @@ -2,7 +2,7 @@ * * DO NOT EDIT THIS FILE (ocserv-args.h) * - * It has been AutoGen-ed February 7, 2013 at 02:45:14 PM by AutoGen 5.16 + * It has been AutoGen-ed February 7, 2013 at 08:20:59 PM by AutoGen 5.16 * From the definitions ocserv-args.def * and the template file options * diff --git a/src/sample.config b/src/sample.config index d4a1e29a..98a32cb1 100644 --- a/src/sample.config +++ b/src/sample.config @@ -52,6 +52,7 @@ tls-priorities = "PERFORMANCE:%SERVER_PRECEDENCE:%COMPAT" # which he can reconnect. This option sets the maximum lifetime # of that cookie. cookie-validity = 14400 +#cookie-db = /tmp/db run-as-user = nobody run-as-group = nogroup diff --git a/src/vpn.h b/src/vpn.h index bf113063..51661a59 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -73,6 +73,9 @@ struct cfg_st { unsigned max_clients; unsigned use_utmp; + /* if gdbm is there */ + const char* cookie_db_name; + const char *connect_script; const char *disconnect_script;