diff --git a/TODO b/TODO index 1b0b47e7..15c9ca03 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ * Add a simple username/password back-end -* Add support for UDP/DTLS * Handle multiple clients in a single tun device (check if needed at all) * Run a server up/down script * Keep the TLS key and certificates into the privileged process and use IPC diff --git a/aclocal.m4 b/aclocal.m4 index cfafe7c0..45594599 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1223,7 +1223,11 @@ AC_SUBST([am__untar]) m4_include([libopts/m4/libopts.m4]) m4_include([gl/m4/00gnulib.m4]) +m4_include([gl/m4/clock_time.m4]) m4_include([gl/m4/extensions.m4]) +m4_include([gl/m4/extern-inline.m4]) +m4_include([gl/m4/gettime.m4]) +m4_include([gl/m4/gettimeofday.m4]) m4_include([gl/m4/gnulib-common.m4]) m4_include([gl/m4/gnulib-comp.m4]) m4_include([gl/m4/include_next.m4]) @@ -1235,5 +1239,9 @@ m4_include([gl/m4/multiarch.m4]) m4_include([gl/m4/stddef_h.m4]) m4_include([gl/m4/stdint.m4]) m4_include([gl/m4/string_h.m4]) +m4_include([gl/m4/sys_socket_h.m4]) +m4_include([gl/m4/sys_time_h.m4]) +m4_include([gl/m4/time_h.m4]) +m4_include([gl/m4/timespec.m4]) m4_include([gl/m4/warn-on-use.m4]) m4_include([gl/m4/wchar_t.m4]) diff --git a/gl/Makefile.am b/gl/Makefile.am index 3f2878ef..da6ceccf 100644 --- a/gl/Makefile.am +++ b/gl/Makefile.am @@ -21,7 +21,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=gl --m4-base=gl/m4 --doc-base=doc --tests-base=gl/tests --aux-dir=build-aux --no-conditional-dependencies --no-libtool --macro-prefix=gl memmem +# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=gl --m4-base=gl/m4 --doc-base=doc --tests-base=gl/tests --aux-dir=build-aux --no-conditional-dependencies --no-libtool --macro-prefix=gl gettime memmem AUTOMAKE_OPTIONS = 1.5 gnits @@ -49,6 +49,21 @@ libgnu_a_LIBADD = $(gl_LIBOBJS) libgnu_a_DEPENDENCIES = $(gl_LIBOBJS) EXTRA_libgnu_a_SOURCES = +## begin gnulib module gettime + +libgnu_a_SOURCES += gettime.c + +## end gnulib module gettime + +## begin gnulib module gettimeofday + + +EXTRA_DIST += gettimeofday.c + +EXTRA_libgnu_a_SOURCES += gettimeofday.c + +## end gnulib module gettimeofday + ## begin gnulib module memchr @@ -316,11 +331,89 @@ EXTRA_DIST += string.in.h ## end gnulib module string -## begin gnulib module dummy +## begin gnulib module sys_time -libgnu_a_SOURCES += dummy.c +BUILT_SOURCES += sys/time.h -## end gnulib module dummy +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +sys/time.h: sys_time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H) + $(AM_V_at)$(MKDIR_P) sys + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's/@''HAVE_SYS_TIME_H''@/$(HAVE_SYS_TIME_H)/g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_SYS_TIME_H''@|$(NEXT_SYS_TIME_H)|g' \ + -e 's/@''GNULIB_GETTIMEOFDAY''@/$(GNULIB_GETTIMEOFDAY)/g' \ + -e 's|@''HAVE_WINSOCK2_H''@|$(HAVE_WINSOCK2_H)|g' \ + -e 's/@''HAVE_GETTIMEOFDAY''@/$(HAVE_GETTIMEOFDAY)/g' \ + -e 's/@''HAVE_STRUCT_TIMEVAL''@/$(HAVE_STRUCT_TIMEVAL)/g' \ + -e 's/@''REPLACE_GETTIMEOFDAY''@/$(REPLACE_GETTIMEOFDAY)/g' \ + -e 's/@''REPLACE_STRUCT_TIMEVAL''@/$(REPLACE_STRUCT_TIMEVAL)/g' \ + -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ + -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ + -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ + < $(srcdir)/sys_time.in.h; \ + } > $@-t && \ + mv $@-t $@ +MOSTLYCLEANFILES += sys/time.h sys/time.h-t + +EXTRA_DIST += sys_time.in.h + +## end gnulib module sys_time + +## begin gnulib module time + +BUILT_SOURCES += time.h + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H) + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_TIME_H''@|$(NEXT_TIME_H)|g' \ + -e 's/@''GNULIB_MKTIME''@/$(GNULIB_MKTIME)/g' \ + -e 's/@''GNULIB_NANOSLEEP''@/$(GNULIB_NANOSLEEP)/g' \ + -e 's/@''GNULIB_STRPTIME''@/$(GNULIB_STRPTIME)/g' \ + -e 's/@''GNULIB_TIMEGM''@/$(GNULIB_TIMEGM)/g' \ + -e 's/@''GNULIB_TIME_R''@/$(GNULIB_TIME_R)/g' \ + -e 's|@''HAVE_DECL_LOCALTIME_R''@|$(HAVE_DECL_LOCALTIME_R)|g' \ + -e 's|@''HAVE_NANOSLEEP''@|$(HAVE_NANOSLEEP)|g' \ + -e 's|@''HAVE_STRPTIME''@|$(HAVE_STRPTIME)|g' \ + -e 's|@''HAVE_TIMEGM''@|$(HAVE_TIMEGM)|g' \ + -e 's|@''REPLACE_LOCALTIME_R''@|$(REPLACE_LOCALTIME_R)|g' \ + -e 's|@''REPLACE_MKTIME''@|$(REPLACE_MKTIME)|g' \ + -e 's|@''REPLACE_NANOSLEEP''@|$(REPLACE_NANOSLEEP)|g' \ + -e 's|@''REPLACE_TIMEGM''@|$(REPLACE_TIMEGM)|g' \ + -e 's|@''PTHREAD_H_DEFINES_STRUCT_TIMESPEC''@|$(PTHREAD_H_DEFINES_STRUCT_TIMESPEC)|g' \ + -e 's|@''SYS_TIME_H_DEFINES_STRUCT_TIMESPEC''@|$(SYS_TIME_H_DEFINES_STRUCT_TIMESPEC)|g' \ + -e 's|@''TIME_H_DEFINES_STRUCT_TIMESPEC''@|$(TIME_H_DEFINES_STRUCT_TIMESPEC)|g' \ + -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ + -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ + -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ + < $(srcdir)/time.in.h; \ + } > $@-t && \ + mv $@-t $@ +MOSTLYCLEANFILES += time.h time.h-t + +EXTRA_DIST += time.in.h + +## end gnulib module time + +## begin gnulib module timespec + +libgnu_a_SOURCES += timespec.c + +EXTRA_DIST += timespec.h + +## end gnulib module timespec mostlyclean-local: mostlyclean-generic diff --git a/gl/dummy.c b/gl/dummy.c deleted file mode 100644 index 4737678a..00000000 --- a/gl/dummy.c +++ /dev/null @@ -1,42 +0,0 @@ -/* A dummy file, to prevent empty libraries from breaking builds. - Copyright (C) 2004, 2007, 2009-2013 Free Software Foundation, Inc. - - 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 . */ - -/* Some systems, reportedly OpenBSD and Mac OS X, refuse to create - libraries without any object files. You might get an error like: - - > ar cru .libs/libgl.a - > ar: no archive members specified - - Compiling this file, and adding its object file to the library, will - prevent the library from being empty. */ - -/* Some systems, such as Solaris with cc 5.0, refuse to work with libraries - that don't export any symbol. You might get an error like: - - > cc ... libgnu.a - > ild: (bad file) garbled symbol table in archive ../gllib/libgnu.a - - Compiling this file, and adding its object file to the library, will - prevent the library from exporting no symbols. */ - -#ifdef __sun -/* This declaration ensures that the library will export at least 1 symbol. */ -int gl_dummy_symbol; -#else -/* This declaration is solely to ensure that after preprocessing - this file is never empty. */ -typedef int dummy; -#endif diff --git a/gl/m4/gnulib-cache.m4 b/gl/m4/gnulib-cache.m4 index 3b354f12..b3bcd7a7 100644 --- a/gl/m4/gnulib-cache.m4 +++ b/gl/m4/gnulib-cache.m4 @@ -27,11 +27,12 @@ # Specification in the form of a command-line invocation: -# gnulib-tool --import --dir=. --lib=libgnu --source-base=gl --m4-base=gl/m4 --doc-base=doc --tests-base=gl/tests --aux-dir=build-aux --no-conditional-dependencies --no-libtool --macro-prefix=gl memmem +# gnulib-tool --import --dir=. --lib=libgnu --source-base=gl --m4-base=gl/m4 --doc-base=doc --tests-base=gl/tests --aux-dir=build-aux --no-conditional-dependencies --no-libtool --macro-prefix=gl gettime memmem # Specification in the form of a few gnulib-tool.m4 macro invocations: gl_LOCAL_DIR([]) gl_MODULES([ + gettime memmem ]) gl_AVOID([]) diff --git a/gl/m4/gnulib-comp.m4 b/gl/m4/gnulib-comp.m4 index 5130c9db..4821fbd6 100644 --- a/gl/m4/gnulib-comp.m4 +++ b/gl/m4/gnulib-comp.m4 @@ -38,8 +38,12 @@ AC_DEFUN([gl_EARLY], m4_pattern_allow([^gl_LIBOBJS$])dnl a variable m4_pattern_allow([^gl_LTLIBOBJS$])dnl a variable AC_REQUIRE([gl_PROG_AR_RANLIB]) + # Code from module clock-time: # Code from module extensions: AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + # Code from module extern-inline: + # Code from module gettime: + # Code from module gettimeofday: # Code from module include_next: # Code from module memchr: # Code from module memmem: @@ -51,6 +55,9 @@ AC_DEFUN([gl_EARLY], # Code from module stddef: # Code from module stdint: # Code from module string: + # Code from module sys_time: + # Code from module time: + # Code from module timespec: ]) # This macro should be invoked from ./configure.ac, in the section @@ -69,6 +76,15 @@ AC_DEFUN([gl_INIT], m4_pushdef([gl_LIBSOURCES_DIR], []) gl_COMMON gl_source_base='gl' + gl_CLOCK_TIME + AC_REQUIRE([gl_EXTERN_INLINE]) + gl_GETTIME + gl_FUNC_GETTIMEOFDAY + if test $HAVE_GETTIMEOFDAY = 0 || test $REPLACE_GETTIMEOFDAY = 1; then + AC_LIBOBJ([gettimeofday]) + gl_PREREQ_GETTIMEOFDAY + fi + gl_SYS_TIME_MODULE_INDICATOR([gettimeofday]) gl_FUNC_MEMCHR if test $HAVE_MEMCHR = 0 || test $REPLACE_MEMCHR = 1; then AC_LIBOBJ([memchr]) @@ -88,6 +104,10 @@ AC_DEFUN([gl_INIT], gl_STDDEF_H gl_STDINT_H gl_HEADER_STRING_H + gl_HEADER_SYS_TIME_H + AC_PROG_MKDIR_P + gl_HEADER_TIME_H + gl_TIMESPEC # End of code from modules m4_ifval(gl_LIBSOURCES_LIST, [ m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ || @@ -231,7 +251,8 @@ AC_DEFUN([gl_FILE_LIST], [ build-aux/snippet/arg-nonnull.h build-aux/snippet/c++defs.h build-aux/snippet/warn-on-use.h - lib/dummy.c + lib/gettime.c + lib/gettimeofday.c lib/memchr.c lib/memchr.valgrind lib/memmem.c @@ -239,8 +260,16 @@ AC_DEFUN([gl_FILE_LIST], [ lib/stdint.in.h lib/str-two-way.h lib/string.in.h + lib/sys_time.in.h + lib/time.in.h + lib/timespec.c + lib/timespec.h m4/00gnulib.m4 + m4/clock_time.m4 m4/extensions.m4 + m4/extern-inline.m4 + m4/gettime.m4 + m4/gettimeofday.m4 m4/gnulib-common.m4 m4/include_next.m4 m4/longlong.m4 @@ -251,6 +280,10 @@ AC_DEFUN([gl_FILE_LIST], [ m4/stddef_h.m4 m4/stdint.m4 m4/string_h.m4 + m4/sys_socket_h.m4 + m4/sys_time_h.m4 + m4/time_h.m4 + m4/timespec.m4 m4/warn-on-use.m4 m4/wchar_t.m4 ]) diff --git a/src/config.c b/src/config.c index 6d8f0bc2..53228335 100644 --- a/src/config.c +++ b/src/config.c @@ -113,7 +113,7 @@ unsigned j; READ_STRING("listen-host", config->name, 0); READ_NUMERIC("tcp-port", config->port, 1); - READ_NUMERIC("udp-port", config->udp_port, 1); + READ_NUMERIC("keepalive", config->keepalive, 0); READ_STRING("server-cert", config->cert, 1); READ_STRING("server-key", config->key, 1); @@ -179,6 +179,9 @@ static void check_cfg( struct cfg_st *config) fprintf(stderr, "No mask found for IPv6 network.\n"); exit(1); } + + if (config->keepalive == 0) + config->keepalive = 30; } int cmd_parser (int argc, char **argv, struct cfg_st* config) diff --git a/src/cookies.h b/src/cookies.h index f3f182a2..6cfc959f 100644 --- a/src/cookies.h +++ b/src/cookies.h @@ -5,11 +5,13 @@ struct __attribute__ ((__packed__)) stored_cookie_st { char username[MAX_USERNAME_SIZE]; + uint8_t master_secret[TLS_MASTER_SIZE]; + uint8_t session_id[GNUTLS_MAX_SESSION_ID]; time_t expiration; }; int store_cookie(const struct cfg_st *, const void* cookie, unsigned cookie_size, - const struct stored_cookie_st* sc); + const struct stored_cookie_st* sc); void expire_cookies(const struct cfg_st *cfg); int retrieve_cookie(const struct cfg_st *, const void* cookie, unsigned cookie_size, diff --git a/src/main-auth.c b/src/main-auth.c index f1068cb6..d1666ad2 100644 --- a/src/main-auth.c +++ b/src/main-auth.c @@ -44,7 +44,7 @@ static int send_auth_reply(cmd_auth_reply_t r, struct proc_list_st* proc, struct lease_st* lease) { - struct iovec iov[4]; + struct iovec iov[6]; uint8_t cmd[2]; struct msghdr hdr; union { @@ -70,12 +70,20 @@ static int send_auth_reply(cmd_auth_reply_t r, struct proc_list_st* proc, struct iov[1].iov_len = sizeof(proc->cookie); hdr.msg_iovlen++; - iov[2].iov_base = lease->name; - iov[2].iov_len = sizeof(lease->name); + iov[2].iov_base = proc->master_secret; + iov[2].iov_len = sizeof(proc->master_secret); hdr.msg_iovlen++; - iov[3].iov_base = proc->username; - iov[3].iov_len = MAX_USERNAME_SIZE; + iov[3].iov_base = proc->session_id; + iov[3].iov_len = sizeof(proc->session_id); + hdr.msg_iovlen++; + + iov[4].iov_base = lease->name; + iov[4].iov_len = sizeof(lease->name); + hdr.msg_iovlen++; + + iov[5].iov_base = proc->username; + iov[5].iov_len = MAX_USERNAME_SIZE; hdr.msg_iovlen++; /* Send the tun fd */ @@ -94,7 +102,7 @@ static int send_auth_reply(cmd_auth_reply_t r, struct proc_list_st* proc, struct static int handle_auth_cookie_req(const struct cfg_st *config, struct tun_st *tun, const struct cmd_auth_cookie_req_st * req, struct lease_st **lease, - char username[MAX_USERNAME_SIZE]) + struct proc_list_st* proc) { int ret; struct stored_cookie_st sc; @@ -106,7 +114,10 @@ struct stored_cookie_st sc; ret = 0; /* cookie was found and valid */ - memcpy(username, sc.username, MAX_USERNAME_SIZE); + memcpy(proc->cookie, req->cookie, sizeof(proc->cookie)); + memcpy(proc->username, sc.username, sizeof(proc->username)); + memcpy(proc->master_secret, sc.master_secret, sizeof(proc->master_secret)); + memcpy(proc->session_id, sc.session_id, sizeof(proc->session_id)); ret = open_tun(config, tun, lease); if (ret < 0) @@ -116,19 +127,29 @@ struct stored_cookie_st sc; } static -int generate_and_store_cookie(const struct cfg_st* config, uint8_t *cookie, unsigned cookie_size) +int generate_and_store_vals(const struct cfg_st* config, struct proc_list_st* proc) { int ret; struct stored_cookie_st sc; - ret = gnutls_rnd(GNUTLS_RND_RANDOM, cookie, cookie_size); + ret = gnutls_rnd(GNUTLS_RND_RANDOM, proc->cookie, sizeof(proc->cookie)); + if (ret < 0) + return -2; + ret = gnutls_rnd(GNUTLS_RND_RANDOM, proc->master_secret, sizeof(proc->master_secret)); + if (ret < 0) + return -2; + ret = gnutls_rnd(GNUTLS_RND_NONCE, proc->session_id, sizeof(proc->session_id)); if (ret < 0) return -2; memset(&sc, 0, sizeof(sc)); sc.expiration = time(0) + config->cookie_validity; - ret = store_cookie(config, cookie, cookie_size, &sc); + memcpy(sc.username, proc->username, sizeof(sc.username)); + memcpy(sc.master_secret, proc->master_secret, sizeof(sc.master_secret)); + memcpy(sc.session_id, proc->session_id, sizeof(sc.session_id)); + + ret = store_cookie(config, proc->cookie, sizeof(proc->cookie), &sc); if (ret < 0) return -1; @@ -233,20 +254,15 @@ int handle_commands(const struct cfg_st *config, struct tun_st *tun, return -2; } - ret = handle_auth_cookie_req(config, tun, &cmd_data.cauth, &lease, proc->username); + ret = handle_auth_cookie_req(config, tun, &cmd_data.cauth, &lease, proc); } if (ret == 0) { if (cmd == AUTH_REQ) { /* generate and store cookie */ - ret = generate_and_store_cookie(config, - proc->cookie, - COOKIE_SIZE); + ret = generate_and_store_vals(config, proc); if (ret < 0) return -2; - } else { /* copy cookie */ - memcpy(proc->cookie, cmd_data.cauth.cookie, - COOKIE_SIZE); } syslog(LOG_INFO, "User '%s' authenticated", proc->username); diff --git a/src/main.c b/src/main.c index 6500d895..84beb98f 100644 --- a/src/main.c +++ b/src/main.c @@ -323,7 +323,7 @@ static void handle_term(int signo) int main(int argc, char** argv) { - int fd, pid; + int fd, pid, e; struct tls_st creds; struct listen_list_st llist; struct proc_list_st clist; @@ -430,6 +430,12 @@ int main(int argc, char** argv) syslog_open = 1; for (;;) { + if (terminate != 0) { + kill_children(&clist); + sleep(1); + exit(0); + } + FD_ZERO(&rd); list_for_each(pos, &llist.list) { @@ -461,11 +467,11 @@ int main(int argc, char** argv) continue; if (ret < 0) { + e = errno; syslog(LOG_ERR, "Error in select(): %s", - strerror(errno)); + strerror(e)); exit(1); } - /* Check for new connections to accept */ list_for_each(pos, &llist.list) { @@ -504,8 +510,9 @@ int main(int argc, char** argv) ws.cmd_fd = cmd_fd[1]; ws.tun_fd = -1; ws.conn_fd = fd; + ws.creds = &creds; - vpn_server(&ws, &creds); + vpn_server(&ws); exit(0); } else if (pid == -1) { fork_failed: @@ -560,12 +567,6 @@ fork_failed: exit(0); } } - - if (terminate != 0) { - kill_children(&clist); - sleep(1); - exit(0); - } } return 0; diff --git a/src/ocserv-args.c b/src/ocserv-args.c index 16421af6..97d4f519 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 January 30, 2013 at 08:53:35 PM by AutoGen 5.16 + * It has been AutoGen-ed January 31, 2013 at 12:25:38 AM 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 cb195cbe..55826ab6 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -60,9 +60,6 @@ auth = "pam" # TCP port number tcp-port = 3333 -# UDP port number -udp-port = 3334 - # The key and the certificates of the server server-cert = /path/to/cert.pem server-key = /path/to/key.pem diff --git a/src/ocserv-args.h b/src/ocserv-args.h index 7bc18219..718d8b84 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 January 30, 2013 at 08:53:35 PM by AutoGen 5.16 + * It has been AutoGen-ed January 31, 2013 at 12:25:38 AM 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 88901ab1..13365ada 100644 --- a/src/sample.config +++ b/src/sample.config @@ -13,8 +13,8 @@ auth-timeout = 40 # TCP port number tcp-port = 3333 -# UDP port number -udp-port = 3334 +# Keepalive in seconds +keepalive = 5 # The key and the certificates of the server server-cert = /home/nmav/cvs/ocserv/test.pem diff --git a/src/vpn.h b/src/vpn.h index 7233b702..a34881eb 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -45,7 +45,6 @@ struct vpn_st { struct cfg_st { const char *name; unsigned int port; - unsigned int udp_port; const char *cert; const char *key; const char *ca; @@ -57,6 +56,7 @@ struct cfg_st { const char *chroot_dir; /* where the xml files are served from */ time_t cookie_validity; /* in seconds */ unsigned auth_timeout; /* timeout of HTTP auth */ + unsigned keepalive; const char *db_file; unsigned foreground; @@ -71,6 +71,7 @@ struct cfg_st { #define MAX_USERNAME_SIZE 64 #define MAX_PASSWORD_SIZE 64 +#define TLS_MASTER_SIZE 48 #define COOKIE_SIZE 32 struct tls_st { @@ -78,8 +79,18 @@ struct tls_st { gnutls_priority_t cprio; }; +typedef enum { + UP_DISABLED, + UP_SETUP, + UP_HANDSHAKE, + UP_INACTIVE, + UP_ACTIVE +} udp_port_state_t; + typedef struct worker_st { + struct tls_st *creds; gnutls_session_t session; + gnutls_session_t dtls_session; int cmd_fd; int conn_fd; @@ -89,10 +100,17 @@ typedef struct worker_st { struct sockaddr_storage remote_addr; /* peer's address */ socklen_t remote_addr_len; + /* set after authentication */ + int udp_fd; + udp_port_state_t udp_state; + unsigned int udp_port; + /* the following are set only if authentication is complete */ char tun_name[IFNAMSIZ]; char username[MAX_USERNAME_SIZE]; uint8_t cookie[COOKIE_SIZE]; + uint8_t master_secret[TLS_MASTER_SIZE]; + uint8_t session_id[GNUTLS_MAX_SESSION_ID]; unsigned auth_ok; int tun_fd; } worker_st; @@ -100,6 +118,7 @@ typedef struct worker_st { enum { HEADER_COOKIE = 1, + HEADER_MASTER_SECRET = 2, }; struct req_data_st { @@ -107,12 +126,14 @@ struct req_data_st { unsigned int next_header; unsigned char cookie[COOKIE_SIZE]; unsigned int cookie_set; + unsigned char master_secret[TLS_MASTER_SIZE]; + unsigned int master_secret_set; char *body; unsigned int headers_complete; unsigned int message_complete; }; -void vpn_server(struct worker_st* ws, struct tls_st *creds); +void vpn_server(struct worker_st* ws); const char *human_addr(const struct sockaddr *sa, socklen_t salen, void *buf, size_t buflen); @@ -130,6 +151,8 @@ struct proc_list_st { socklen_t remote_addr_len; char username[MAX_USERNAME_SIZE]; /* the owner */ uint8_t cookie[COOKIE_SIZE]; /* the cookie associate with the session */ + uint8_t master_secret[TLS_MASTER_SIZE]; + uint8_t session_id[GNUTLS_MAX_SESSION_ID]; /* the tun lease this process has */ struct lease_st* lease; @@ -144,4 +167,7 @@ int handle_commands(const struct cfg_st *config, struct tun_st *tun, #define SA_IN6_P(p) (&((struct sockaddr_in6 *)(p))->sin6_addr) #define SA_IN6_U8_P(p) ((uint8_t*)(&((struct sockaddr_in6 *)(p))->sin6_addr)) +#define SA_IN_PORT(p) (((struct sockaddr_in *)(p))->sin_port) +#define SA_IN6_PORT(p) (((struct sockaddr_in6 *)(p))->sin6_port) + #endif diff --git a/src/worker-auth.c b/src/worker-auth.c index d6290ca8..47e10c82 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -193,6 +193,8 @@ static int recv_auth_reply(worker_st *ws) memcpy(ws->tun_name, resp.vname, sizeof(ws->tun_name)); memcpy(ws->username, resp.user, sizeof(ws->username)); memcpy(ws->cookie, resp.cookie, sizeof(ws->cookie)); + memcpy(ws->master_secret, resp.master_secret, sizeof(ws->master_secret)); + memcpy(ws->session_id, resp.session_id, sizeof(ws->session_id)); ws->auth_ok = 1; } else return -1; @@ -386,7 +388,6 @@ int post_new_auth_handler(worker_st *ws) int ret; struct req_data_st *req = ws->parser->data; const char* reason = "Authentication failed"; -unsigned char cookie[COOKIE_SIZE]; char str_cookie[2*COOKIE_SIZE+1]; char * username = NULL; char * password = NULL; @@ -447,13 +448,9 @@ struct cmd_auth_req_st areq; oclog(ws, LOG_INFO, "User '%s' logged in\n", ws->username); - /* generate cookie */ - ret = gnutls_rnd(GNUTLS_RND_RANDOM, cookie, sizeof(cookie)); - GNUTLS_FATAL_ERR(ret); - p = str_cookie; - for (i=0;icookie);i++) { + sprintf(p, "%.2x", (unsigned int)ws->cookie[i]); p+=2; } diff --git a/src/worker-auth.h b/src/worker-auth.h index fa403988..16af2c9c 100644 --- a/src/worker-auth.h +++ b/src/worker-auth.h @@ -38,18 +38,12 @@ struct __attribute__ ((__packed__)) cmd_auth_req_st { char cert_user[MAX_USERNAME_SIZE]; }; -struct __attribute__ ((__packed__)) cmd_udp_fd_st { - uint8_t user_pass_present; - char user[MAX_USERNAME_SIZE]; - char pass[MAX_PASSWORD_SIZE]; - uint8_t tls_auth_ok; - char cert_user[MAX_USERNAME_SIZE]; -}; - /* AUTH_REP */ struct __attribute__ ((__packed__)) cmd_auth_reply_st { uint8_t reply; uint8_t cookie[COOKIE_SIZE]; + uint8_t master_secret[TLS_MASTER_SIZE]; + uint8_t session_id[GNUTLS_MAX_SESSION_ID]; char vname[IFNAMSIZ]; /* interface name */ char user[MAX_USERNAME_SIZE]; }; diff --git a/src/worker-vpn.c b/src/worker-vpn.c index 9819703c..02d682e5 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include #include @@ -37,6 +39,7 @@ #include #include #include +#include #include #include @@ -45,10 +48,16 @@ #include +/* after that time (secs) of inactivity in the UDP part, connection switches to + * TCP (if activity occurs there). + */ +#define UDP_SWITCH_TIME 15 + /* HTTP requests prior to disconnection */ #define MAX_HTTP_REQUESTS 8 static int handle_worker_commands(struct worker_st *ws); +static int parse_cstp_data(struct worker_st* ws, uint8_t* buf, size_t buf_size); static void handle_alarm(int signo) { @@ -119,8 +128,10 @@ int header_field_cb(http_parser* parser, const char *at, size_t length) { struct req_data_st *req = parser->data; - if (strncmp(at, "Cookie", length) == 0) { + if (strncmp(at, "Cookie:", length) == 0) { req->next_header = HEADER_COOKIE; + } else if (strncmp(at, "X-DTLS-Master-Secret:", length) == 0) { + req->next_header = HEADER_MASTER_SECRET; } else { req->next_header = 0; } @@ -136,6 +147,19 @@ size_t nlen; if (length > 0) switch (req->next_header) { + case HEADER_MASTER_SECRET: + if (length < TLS_MASTER_SIZE*2) { + req->master_secret_set = 0; + return 0; + } + + length = TLS_MASTER_SIZE*2; + + nlen = sizeof(req->master_secret); + + gnutls_hex2bin(at, length, req->master_secret, &nlen); + req->master_secret_set = 1; + break; case HEADER_COOKIE: p = memmem(at, length, "webvpn=", 7); if (p == NULL || length <= 7) { @@ -191,7 +215,97 @@ char* tmp = malloc(length+1); return 0; } -void vpn_server(struct worker_st* ws, struct tls_st *creds) +#define GNUTLS_CIPHERSUITE "NONE:+VERS-DTLS0.9:+COMP-NULL:+AES-128-CBC:+SHA1:+RSA:%COMPAT:%DISABLE_SAFE_RENEGOTIATION" +#define OPENSSL_CIPHERSUITE "AES128-SHA" + +static int setup_dtls_connection(struct worker_st *ws) +{ +int ret, e; +gnutls_session_t session; +struct sockaddr_storage cli_addr; +socklen_t cli_addr_size; +uint8_t buffer[512]; +ssize_t buffer_size; +gnutls_datum_t master = { ws->master_secret, sizeof(ws->master_secret) }; +gnutls_datum_t sid = { ws->session_id, sizeof(ws->session_id) }; + + /* first receive from the correct client and connect socket */ + cli_addr_size = sizeof(cli_addr); + ret = recvfrom(ws->udp_fd, buffer, sizeof(buffer), MSG_PEEK, (void*)&cli_addr, &cli_addr_size); + if (ret < 0) { + oclog(ws, LOG_ERR, "Error receiving in UDP socket"); + return -1; + } + + buffer_size = ret; + + if ( (ws->remote_addr_len == sizeof(struct sockaddr_in) && memcmp(SA_IN_P(&cli_addr), + SA_IN_P(&ws->remote_addr), sizeof(struct in_addr)) == 0) || + (ws->remote_addr_len == sizeof(struct sockaddr_in6) && memcmp(SA_IN6_P(&cli_addr), + SA_IN6_P(&ws->remote_addr), sizeof(struct in6_addr)) == 0)) { + + /* connect to host */ + ret = connect(ws->udp_fd, (void*)&cli_addr, cli_addr_size); + if (ret < 0) { + e = errno; + oclog(ws, LOG_ERR, "Error connecting: %s", strerror(e)); + return -1; + } + } else { + /* received packet from unknown host */ + + oclog(ws, LOG_ERR, "Received UDP packet from unexpected host; discarding it"); + recv(ws->udp_fd, buffer, buffer_size, 0); + + return 0; + } + + /* DTLS cookie verified. + * Initialize session. + */ + ret = gnutls_init(&session, GNUTLS_SERVER|GNUTLS_DATAGRAM); + if (ret < 0) { + oclog(ws, LOG_ERR, "Could not initialize TLS session: %s", gnutls_strerror(ret)); + return -1; + } + + ret = gnutls_priority_set_direct(session, GNUTLS_CIPHERSUITE, NULL); + if (ret < 0) { + oclog(ws, LOG_ERR, "Could not set TLS priority: %s", gnutls_strerror(ret)); + goto fail; + } + + ret = gnutls_session_set_premaster(session, GNUTLS_SERVER, + GNUTLS_DTLS0_9, GNUTLS_KX_RSA, GNUTLS_CIPHER_AES_128_CBC, + GNUTLS_MAC_SHA1, GNUTLS_COMP_NULL, &master, &sid); + if (ret < 0) { + oclog(ws, LOG_ERR, "Could not set TLS premaster: %s", gnutls_strerror(ret)); + goto fail; + } + + + ret = + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + ws->creds->xcred); + if (ret < 0) { + oclog(ws, LOG_ERR, "Could not set TLS credentials: %s", gnutls_strerror(ret)); + goto fail; + } + + gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) (long)ws->udp_fd); + gnutls_session_set_ptr(session, ws); + + ws->udp_state = UP_HANDSHAKE; + + ws->dtls_session = session; + + return 0; +fail: + gnutls_deinit(session); + return -1; +} + +void vpn_server(struct worker_st* ws) { unsigned char buf[2048]; int ret; @@ -218,12 +332,12 @@ void vpn_server(struct worker_st* ws, struct tls_st *creds) ret = gnutls_init(&session, GNUTLS_SERVER); GNUTLS_FATAL_ERR(ret); - ret = gnutls_priority_set(session, creds->cprio); + ret = gnutls_priority_set(session, ws->creds->cprio); GNUTLS_FATAL_ERR(ret); ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, - creds->xcred); + ws->creds->xcred); GNUTLS_FATAL_ERR(ret); gnutls_certificate_server_set_request(session, ws->config->cert_req); @@ -249,7 +363,7 @@ void vpn_server(struct worker_st* ws, struct tls_st *creds) restart: if (requests_left-- <= 0) { - oclog(ws, LOG_INFO, "Maximum number of HTTP requests reached."); + oclog(ws, LOG_INFO, "Maximum number of HTTP requests reached"); exit(1); } @@ -448,7 +562,7 @@ struct ifreq ifr; snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name); ret = ioctl(fd, SIOCGIFMTU, (caddr_t)&ifr); if (ret < 0) { - oclog(ws, LOG_ERR, "Cannot obtain MTU for %s. Assuming 1500.", vinfo->name); + oclog(ws, LOG_ERR, "Cannot obtain MTU for %s. Assuming 1500", vinfo->name); vinfo->mtu = 1500; } else { vinfo->mtu = ifr.ifr_mtu; @@ -460,19 +574,120 @@ fail: return ret; } +static int open_udp_port(worker_st *ws) +{ +int s, e, ret; +struct sockaddr_storage si; +struct sockaddr_storage stcp; +socklen_t len; +int proto; + + len = sizeof(stcp); + ret = getsockname(ws->conn_fd, (void*)&stcp, &len); + if (ret == -1) { + e = errno; + oclog(ws, LOG_ERR, "Error in getsockname: %s", strerror(e)); + return -1; + } + + + proto = ((struct sockaddr*)&stcp)->sa_family; + + s = socket(proto, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + e = errno; + oclog(ws, LOG_ERR, "Could not open a UDP socket: %s", strerror(e)); + return -1; + } + + /* listen on the same IP the client connected at */ + memset(&si, 0, sizeof(si)); + ((struct sockaddr*)&stcp)->sa_family = proto; + +#if 0 + if (proto == AF_INET) { + memcpy(SA_IN_P(&si), SA_IN_P(&stcp), len); + } else if (proto == AF_INET6) { + memcpy(SA_IN6_P(&si), SA_IN6_P(&stcp), len); + } else { + oclog(ws, LOG_ERR, "Unknown protocol family: %d", proto); + goto fail; + } +#endif + + /* make sure we don't fragment packets */ +#if defined(IP_DONTFRAG) + ret = 1; + if (setsockopt (s, IPPROTO_IP, IP_DONTFRAG, + (const void *) &ret, sizeof (ret)) < 0) + if (ret < 0) { + e = errno; + oclog(ws, LOG_ERR, "Error in setsockopt (IP_DF): %s", strerror(e)); + goto fail; + } +#elif defined(IP_MTU_DISCOVER) + ret = IP_PMTUDISC_DO; + if (setsockopt (s, IPPROTO_IP, IP_MTU_DISCOVER, + (const void *) &ret, sizeof (ret)) < 0) + if (ret < 0) { + e = errno; + oclog(ws, LOG_ERR, "Error in setsockopt (IP_MTU_DISCOVER): %s", strerror(e)); + goto fail; + } +#endif +#ifdef SO_REUSEPORT + ret = 1; + ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &ret, sizeof(ret)); + if (ret < 0) { + e = errno; + oclog(ws, LOG_ERR, "Error in setsockopt (SO_REUSEPORT): %s", strerror(e)); + goto fail; + } +#endif + ret = bind(s, (void*)&si, len); + if (ret == -1) { + e = errno; + oclog(ws, LOG_ERR, "Could not bind on a UDP port: %s", strerror(e)); + goto fail; + } + + len = sizeof(si); + ret = getsockname(s, (void*)&si, &len); + if (ret == -1) { + e = errno; + oclog(ws, LOG_ERR, "Could not obtain UDP port number: %s", strerror(e)); + goto fail; + } + + if (proto == AF_INET) { + ws->udp_port = ntohs(SA_IN_PORT(&si)); + } else { + ws->udp_port = ntohs(SA_IN6_PORT(&si)); + } + + ws->udp_fd = s; + + return 0; +fail: + close(s); + return -1; +} + + static int connect_handler(worker_st *ws) { int ret; struct req_data_st *req = ws->parser->data; -char buf[256]; fd_set rfds; -int l, pktlen, e; -int tls_fd, max; +int l, e; +int max; unsigned i; struct vpn_st vinfo; -char* buffer; +uint8_t buffer[1024]; unsigned int buffer_size; -int tls_pending; +char *p; +unsigned tls_pending, dtls_pending = 0; +time_t udp_recv_time = 0; if (req->cookie_set == 0) { oclog(ws, LOG_INFO, "Connect request without authentication"); @@ -505,23 +720,16 @@ int tls_pending; } if (ws->config->network.name == NULL) { - oclog(ws, LOG_ERR, "No networks are configured. Rejecting client."); + oclog(ws, LOG_ERR, "No networks are configured. Rejecting client"); tls_puts(ws->session, "HTTP/1.1 503 Service Unavailable\r\n"); tls_puts(ws->session, "X-Reason: Server configuration error\r\n\r\n"); return -1; } - buffer_size = 2048; - buffer = malloc(buffer_size); - if (buffer == NULL) { - oclog(ws, LOG_ERR, "Memory error. Rejecting client."); - tls_puts(ws->session, "HTTP/1.1 503 Service Unavailable\r\n\r\n"); - return -1; - } - - ret = get_rt_vpn_info(ws, &vinfo, buffer, buffer_size); + buffer_size = sizeof(buffer); + ret = get_rt_vpn_info(ws, &vinfo, (char*)buffer, buffer_size); if (ret < 0) { - oclog(ws, LOG_ERR, "Network interfaces are not configured. Rejecting client."); + oclog(ws, LOG_ERR, "Network interfaces are not configured. Rejecting client"); tls_puts(ws->session, "HTTP/1.1 503 Service Unavailable\r\n"); tls_puts(ws->session, "X-Reason: Server configuration error\r\n\r\n"); @@ -534,6 +742,18 @@ int tls_pending; tls_printf(ws->session, "X-CSTP-MTU: %u\r\n", vinfo.mtu); tls_puts(ws->session, "X-CSTP-DPD: 60\r\n"); + ws->udp_state = UP_DISABLED; + if (req->master_secret_set != 0) { + memcpy(ws->master_secret, req->master_secret, TLS_MASTER_SIZE); + + ret = open_udp_port(ws); + if (ret < 0) { + oclog(ws, LOG_NOTICE, "Could not open UDP port"); + } else + ws->udp_state = UP_SETUP; + } + + if (vinfo.ipv4) { oclog(ws, LOG_DEBUG, "sending IPv4 %s", vinfo.ipv4); tls_printf(ws->session, "X-CSTP-Address: %s\r\n", vinfo.ipv4); @@ -553,37 +773,142 @@ int tls_pending; if (vinfo.ipv6_dns) tls_printf(ws->session, "X-CSTP-DNS: %s\r\n", vinfo.ipv6_dns); } - + for (i=0;isession, "X-CSTP-Split-Include: %s\r\n", vinfo.routes[i]); } + tls_printf(ws->session, "X-CSTP-Keepalive: %u\r\n", ws->config->keepalive); + + if (ws->udp_state != UP_DISABLED) { + p = (char*)buffer; + for (i=0;isession_id);i++) { + sprintf(p, "%.2x", (unsigned int)ws->session_id[i]); + p+=2; + } + tls_printf(ws->session, "X-DTLS-Session-ID: %s\r\n", buffer); + + p = (char*)buffer; + for (i=0;imaster_secret);i++) { + sprintf(p, "%.2x", (unsigned int)ws->master_secret[i]); + p+=2; + } +fprintf(stderr, "X-DTLS-Master-Secret: %s\n", buffer); + +// tls_printf(ws->session, "X-DTLS-Master-Secret: %s\r\n", buffer); + + tls_printf(ws->session, "X-DTLS-Port: %u\r\n", ws->udp_port); + tls_puts(ws->session, "X-DTLS-ReKey-Time: 86400\r\n"); + tls_printf(ws->session, "X-DTLS-Keepalive: %u\r\n", ws->config->keepalive); + tls_puts(ws->session, "X-DTLS-CipherSuite: "OPENSSL_CIPHERSUITE"\r\n"); + } + tls_puts(ws->session, "X-CSTP-Banner: Hello there\r\n"); tls_puts(ws->session, "\r\n"); - free(buffer); - buffer = NULL; - - tls_fd = (long)gnutls_transport_get_ptr(ws->session); - for(;;) { - tls_pending = 0; FD_ZERO(&rfds); - FD_SET(tls_fd, &rfds); + FD_SET(ws->conn_fd, &rfds); FD_SET(ws->cmd_fd, &rfds); FD_SET(ws->tun_fd, &rfds); - max = MAX(ws->cmd_fd,tls_fd); + max = MAX(ws->cmd_fd,ws->conn_fd); max = MAX(max,ws->tun_fd); + if (ws->udp_state != UP_DISABLED) { + FD_SET(ws->udp_fd, &rfds); + max = MAX(max,ws->udp_fd); + } + tls_pending = gnutls_record_check_pending(ws->session); - if (tls_pending == 0) { + + if (ws->dtls_session != NULL) + dtls_pending = gnutls_record_check_pending(ws->dtls_session); + if (tls_pending == 0 && dtls_pending == 0) { ret = select(max + 1, &rfds, NULL, NULL, NULL); if (ret <= 0) break; } + if (FD_ISSET(ws->tun_fd, &rfds)) { + l = read(ws->tun_fd, buffer + 8, sizeof(buffer) - 8); + if (l <= 0) { + e = errno; + oclog(ws, LOG_ERR, "Received corrupt data from tun (%d): %s", l, strerror(e)); + exit(1); + } + buffer[0] = 'S'; + buffer[1] = 'T'; + buffer[2] = 'F'; + buffer[3] = 1; + buffer[4] = l >> 8; + buffer[5] = l & 0xff; + buffer[6] = 0; + buffer[7] = 0; + + if (ws->udp_state == UP_ACTIVE) + ret = tls_send(ws->dtls_session, buffer, l + 8); + else + ret = tls_send(ws->session, buffer, l + 8); + GNUTLS_FATAL_ERR(ret); + } + + if (FD_ISSET(ws->conn_fd, &rfds) || tls_pending != 0) { + ret = tls_recv(ws->session, buffer, sizeof(buffer)); + GNUTLS_FATAL_ERR(ret); + + ret = parse_cstp_data(ws, buffer, ret); + if (ret < 0) { + exit(1); + } + + if (ret == AC_PKT_DATA && ws->udp_state == UP_ACTIVE) { + /* client switched to TLS for some reason */ + if (time(0) - udp_recv_time > UDP_SWITCH_TIME) + ws->udp_state = UP_INACTIVE; + } + } + + if (ws->udp_state != UP_DISABLED && (FD_ISSET(ws->udp_fd, &rfds) || dtls_pending != 0)) { + + switch (ws->udp_state) { + case UP_ACTIVE: + case UP_INACTIVE: + ret = tls_recv(ws->dtls_session, buffer, sizeof(buffer)); + GNUTLS_FATAL_ERR(ret); + + ws->udp_state = UP_ACTIVE; + + ret = parse_cstp_data(ws, buffer, ret); + if (ret < 0) + exit(1); + + udp_recv_time = time(0); + break; + case UP_SETUP: + ret = setup_dtls_connection(ws); + if (ret < 0) + exit(1); + break; + case UP_HANDSHAKE: + ret = gnutls_handshake(ws->dtls_session); + + if (ret < 0 && gnutls_error_is_fatal(ret) != 0) { + oclog(ws, LOG_ERR, "Error in DTLS handshake: %s\n", gnutls_strerror(ret)); + ws->udp_state = UP_DISABLED; + break; + } + + if (ret == 0) + ws->udp_state = UP_ACTIVE; + + break; + default: + break; + } + } + if (FD_ISSET(ws->cmd_fd, &rfds)) { ret = handle_worker_commands(ws); if (ret < 0) { @@ -591,79 +916,6 @@ int tls_pending; } } - if (FD_ISSET(ws->tun_fd, &rfds)) { - l = read(ws->tun_fd, buf + 8, sizeof(buf) - 8); - if (l <= 0) { - e = errno; - oclog(ws, LOG_ERR, "Received corrupt data from tun (%d): %s", l, strerror(e)); - exit(1); - } - buf[0] = 'S'; - buf[1] = 'T'; - buf[2] = 'F'; - buf[3] = 1; - buf[4] = l >> 8; - buf[5] = l & 0xff; - buf[6] = 0; - buf[7] = 0; - - ret = tls_send(ws->session, buf, l + 8); - GNUTLS_FATAL_ERR(ret); - } - - if (FD_ISSET(tls_fd, &rfds) || tls_pending != 0) { - l = tls_recv(ws->session, buf, sizeof(buf)); - GNUTLS_FATAL_ERR(l); - - if (l < 8) { - oclog(ws, LOG_INFO, - "Can't read CSTP header\n"); - exit(1); - } - if (buf[0] != 'S' || buf[1] != 'T' || - buf[2] != 'F' || buf[3] != 1 || buf[7]) { - oclog(ws, LOG_INFO, - "Can't recognise CSTP header\n"); - exit(1); - } - pktlen = (buf[4] << 8) + buf[5]; - if (l != 8 + pktlen) { - oclog(ws, LOG_INFO, "Unexpected length\n"); - exit(1); - } - switch (buf[6]) { - case AC_PKT_DPD_RESP: - case AC_PKT_KEEPALIVE: - break; - - case AC_PKT_DPD_OUT: - ret = - tls_send(ws->session, "STF\x1\x0\x0\x4\x0", - 8); - GNUTLS_FATAL_ERR(ret); - break; - - case AC_PKT_DISCONN: - oclog(ws, LOG_INFO, "Received BYE packet\n"); - break; - - case AC_PKT_DATA: - l = write(ws->tun_fd, buf + 8, pktlen); - if (l == -1) { - e = errno; - oclog(ws, LOG_ERR, "Could not write data to tun: %s", strerror(e)); - exit(1); - } - - if (l != pktlen) { - oclog(ws, LOG_ERR, "Could not write all data to tun"); - exit(1); - } - break; - } - } - - } @@ -696,7 +948,7 @@ int handle_worker_commands(struct worker_st *ws) ret = recvmsg( ws->cmd_fd, &hdr, 0); if (ret == -1) { - oclog(ws, LOG_ERR, "Cannot obtain data from command socket."); + oclog(ws, LOG_ERR, "Cannot obtain data from command socket"); exit(1); } @@ -710,9 +962,61 @@ int handle_worker_commands(struct worker_st *ws) case CMD_TERMINATE: exit(0); default: - oclog(ws, LOG_ERR, "Unknown CMD 0x%x.", (unsigned)cmd); + oclog(ws, LOG_ERR, "Unknown CMD 0x%x", (unsigned)cmd); exit(1); } return 0; } + +static int parse_cstp_data(struct worker_st* ws, uint8_t* buf, size_t buf_size) +{ +int pktlen, ret, e; + + if (buf_size < 8) { + oclog(ws, LOG_INFO, "Can't read CSTP header\n"); + return -1; + } + + if (buf[0] != 'S' || buf[1] != 'T' || + buf[2] != 'F' || buf[3] != 1 || buf[7]) { + oclog(ws, LOG_INFO, "Can't recognise CSTP header\n"); + return -1; + } + + pktlen = (buf[4] << 8) + buf[5]; + if (buf_size != 8 + pktlen) { + oclog(ws, LOG_INFO, "Unexpected length\n"); + return -1; + } + + switch (buf[6]) { + case AC_PKT_DPD_RESP: + case AC_PKT_KEEPALIVE: + break; + + case AC_PKT_DPD_OUT: + ret = + tls_send(ws->session, "STF\x1\x0\x0\x4\x0", 8); + GNUTLS_FATAL_ERR(ret); + break; + case AC_PKT_DISCONN: + oclog(ws, LOG_INFO, "Received BYE packet\n"); + break; + case AC_PKT_DATA: + ret = write(ws->tun_fd, buf + 8, pktlen); + if (ret == -1) { + e = errno; + oclog(ws, LOG_ERR, "Could not write data to tun: %s", strerror(e)); + return -1; + } + + if (ret != pktlen) { + oclog(ws, LOG_ERR, "Could not write all data to tun"); + exit(1); + } + break; + } + + return buf[6]; +}