summaryrefslogtreecommitdiff
path: root/src/openvpnserv
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpnserv')
-rw-r--r--src/openvpnserv/Makefile.am16
-rw-r--r--src/openvpnserv/Makefile.in181
-rw-r--r--src/openvpnserv/automatic.c415
-rw-r--r--src/openvpnserv/common.c218
-rw-r--r--src/openvpnserv/interactive.c1652
-rwxr-xr-xsrc/openvpnserv/openvpnserv.c534
-rw-r--r--src/openvpnserv/openvpnserv.vcxproj2
-rw-r--r--src/openvpnserv/service.c861
-rw-r--r--src/openvpnserv/service.h215
-rw-r--r--src/openvpnserv/validate.c249
-rw-r--r--src/openvpnserv/validate.h48
11 files changed, 3028 insertions, 1363 deletions
diff --git a/src/openvpnserv/Makefile.am b/src/openvpnserv/Makefile.am
index a989c25..3521a34 100644
--- a/src/openvpnserv/Makefile.am
+++ b/src/openvpnserv/Makefile.am
@@ -17,11 +17,23 @@ EXTRA_DIST = \
openvpnserv.vcxproj \
openvpnserv.vcxproj.filters
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include -I$(top_srcdir)/src/openvpn -I$(top_srcdir)/src/compat
+
if WIN32
sbin_PROGRAMS = openvpnserv
+openvpnserv_CFLAGS = \
+ -municode -D_UNICODE \
+ -UNTDDI_VERSION -U_WIN32_WINNT \
+ -D_WIN32_WINNT=_WIN32_WINNT_VISTA
+openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32
endif
openvpnserv_SOURCES = \
- openvpnserv.c \
- service.h service.c \
+ common.c \
+ automatic.c \
+ interactive.c \
+ service.c service.h \
+ validate.c validate.h \
+ $(top_srcdir)/src/openvpn/block_dns.c $(top_srcdir)/src/openvpn/block_dns.h \
openvpnserv_resources.rc
diff --git a/src/openvpnserv/Makefile.in b/src/openvpnserv/Makefile.in
index ed1ee90..74a802b 100644
--- a/src/openvpnserv/Makefile.in
+++ b/src/openvpnserv/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -37,17 +37,7 @@
# Required to build Windows resource file
VPATH = @srcdir@
-am__is_gnu_make = { \
- if test -z '$(MAKELEVEL)'; then \
- false; \
- elif test -n '$(MAKE_HOST)'; then \
- true; \
- elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
- true; \
- else \
- false; \
- fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
@@ -110,6 +100,8 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
+DIST_COMMON = $(top_srcdir)/build/ltrc.inc $(srcdir)/Makefile.in \
+ $(srcdir)/Makefile.am $(top_srcdir)/depcomp
@WIN32_TRUE@sbin_PROGRAMS = openvpnserv$(EXEEXT)
subdir = src/openvpnserv
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -122,21 +114,28 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \
$(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_HEADER = $(top_builddir)/config.h \
+ $(top_builddir)/include/openvpn-plugin.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(sbindir)"
PROGRAMS = $(sbin_PROGRAMS)
-am_openvpnserv_OBJECTS = openvpnserv.$(OBJEXT) service.$(OBJEXT) \
+am_openvpnserv_OBJECTS = openvpnserv-common.$(OBJEXT) \
+ openvpnserv-automatic.$(OBJEXT) \
+ openvpnserv-interactive.$(OBJEXT) \
+ openvpnserv-service.$(OBJEXT) openvpnserv-validate.$(OBJEXT) \
+ openvpnserv-block_dns.$(OBJEXT) \
openvpnserv_resources.$(OBJEXT)
openvpnserv_OBJECTS = $(am_openvpnserv_OBJECTS)
-openvpnserv_LDADD = $(LDADD)
+openvpnserv_DEPENDENCIES =
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
+openvpnserv_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(openvpnserv_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
@@ -149,7 +148,7 @@ AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
-DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/include
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
@@ -197,8 +196,6 @@ am__define_uniq_tagged_files = \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/ltrc.inc \
- $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
@@ -212,6 +209,7 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
+CMAKE = @CMAKE@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -246,25 +244,31 @@ LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
-LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LZ4_CFLAGS = @LZ4_CFLAGS@
+LZ4_LIBS = @LZ4_LIBS@
LZO_CFLAGS = @LZO_CFLAGS@
LZO_LIBS = @LZO_LIBS@
MAKEINFO = @MAKEINFO@
MAN2HTML = @MAN2HTML@
MANIFEST_TOOL = @MANIFEST_TOOL@
+MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@
+MBEDTLS_LIBS = @MBEDTLS_LIBS@
MKDIR_P = @MKDIR_P@
NETSTAT = @NETSTAT@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
-OPENSSL_CRYPTO_CFLAGS = @OPENSSL_CRYPTO_CFLAGS@
-OPENSSL_CRYPTO_LIBS = @OPENSSL_CRYPTO_LIBS@
-OPENSSL_SSL_CFLAGS = @OPENSSL_SSL_CFLAGS@
-OPENSSL_SSL_LIBS = @OPENSSL_SSL_LIBS@
+OPENSSL_CFLAGS = @OPENSSL_CFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@
+OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@
+OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@
OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@
OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@
OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@
+OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@
+OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@
OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@
OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@
OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@
@@ -290,8 +294,6 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@
PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
-POLARSSL_CFLAGS = @POLARSSL_CFLAGS@
-POLARSSL_LIBS = @POLARSSL_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
@@ -306,6 +308,11 @@ TAP_CFLAGS = @TAP_CFLAGS@
TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@
TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@
TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@
+TEST_CFLAGS = @TEST_CFLAGS@
+TEST_LDFLAGS = @TEST_LDFLAGS@
+VENDOR_BUILD_ROOT = @VENDOR_BUILD_ROOT@
+VENDOR_DIST_ROOT = @VENDOR_DIST_ROOT@
+VENDOR_SRC_ROOT = @VENDOR_SRC_ROOT@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
@@ -372,9 +379,22 @@ EXTRA_DIST = \
openvpnserv.vcxproj \
openvpnserv.vcxproj.filters
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include -I$(top_srcdir)/src/openvpn -I$(top_srcdir)/src/compat
+
+@WIN32_TRUE@openvpnserv_CFLAGS = \
+@WIN32_TRUE@ -municode -D_UNICODE \
+@WIN32_TRUE@ -UNTDDI_VERSION -U_WIN32_WINNT \
+@WIN32_TRUE@ -D_WIN32_WINNT=_WIN32_WINNT_VISTA
+
+@WIN32_TRUE@openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32
openvpnserv_SOURCES = \
- openvpnserv.c \
- service.h service.c \
+ common.c \
+ automatic.c \
+ interactive.c \
+ service.c service.h \
+ validate.c validate.h \
+ $(top_srcdir)/src/openvpn/block_dns.c $(top_srcdir)/src/openvpn/block_dns.h \
openvpnserv_resources.rc
all: all-am
@@ -393,6 +413,7 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build/ltrc.inc $(am_
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/openvpnserv/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign src/openvpnserv/Makefile
+.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -401,7 +422,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
-$(top_srcdir)/build/ltrc.inc $(am__empty):
+$(top_srcdir)/build/ltrc.inc:
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -463,7 +484,7 @@ clean-sbinPROGRAMS:
openvpnserv$(EXEEXT): $(openvpnserv_OBJECTS) $(openvpnserv_DEPENDENCIES) $(EXTRA_openvpnserv_DEPENDENCIES)
@rm -f openvpnserv$(EXEEXT)
- $(AM_V_CCLD)$(LINK) $(openvpnserv_OBJECTS) $(openvpnserv_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(openvpnserv_LINK) $(openvpnserv_OBJECTS) $(openvpnserv_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -471,22 +492,26 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-automatic.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-block_dns.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-interactive.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-validate.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -495,6 +520,90 @@ distclean-compile:
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+openvpnserv-common.o: common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-common.o -MD -MP -MF $(DEPDIR)/openvpnserv-common.Tpo -c -o openvpnserv-common.o `test -f 'common.c' || echo '$(srcdir)/'`common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-common.Tpo $(DEPDIR)/openvpnserv-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='common.c' object='openvpnserv-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-common.o `test -f 'common.c' || echo '$(srcdir)/'`common.c
+
+openvpnserv-common.obj: common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-common.obj -MD -MP -MF $(DEPDIR)/openvpnserv-common.Tpo -c -o openvpnserv-common.obj `if test -f 'common.c'; then $(CYGPATH_W) 'common.c'; else $(CYGPATH_W) '$(srcdir)/common.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-common.Tpo $(DEPDIR)/openvpnserv-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='common.c' object='openvpnserv-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-common.obj `if test -f 'common.c'; then $(CYGPATH_W) 'common.c'; else $(CYGPATH_W) '$(srcdir)/common.c'; fi`
+
+openvpnserv-automatic.o: automatic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-automatic.o -MD -MP -MF $(DEPDIR)/openvpnserv-automatic.Tpo -c -o openvpnserv-automatic.o `test -f 'automatic.c' || echo '$(srcdir)/'`automatic.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-automatic.Tpo $(DEPDIR)/openvpnserv-automatic.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='automatic.c' object='openvpnserv-automatic.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-automatic.o `test -f 'automatic.c' || echo '$(srcdir)/'`automatic.c
+
+openvpnserv-automatic.obj: automatic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-automatic.obj -MD -MP -MF $(DEPDIR)/openvpnserv-automatic.Tpo -c -o openvpnserv-automatic.obj `if test -f 'automatic.c'; then $(CYGPATH_W) 'automatic.c'; else $(CYGPATH_W) '$(srcdir)/automatic.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-automatic.Tpo $(DEPDIR)/openvpnserv-automatic.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='automatic.c' object='openvpnserv-automatic.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-automatic.obj `if test -f 'automatic.c'; then $(CYGPATH_W) 'automatic.c'; else $(CYGPATH_W) '$(srcdir)/automatic.c'; fi`
+
+openvpnserv-interactive.o: interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-interactive.o -MD -MP -MF $(DEPDIR)/openvpnserv-interactive.Tpo -c -o openvpnserv-interactive.o `test -f 'interactive.c' || echo '$(srcdir)/'`interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-interactive.Tpo $(DEPDIR)/openvpnserv-interactive.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interactive.c' object='openvpnserv-interactive.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-interactive.o `test -f 'interactive.c' || echo '$(srcdir)/'`interactive.c
+
+openvpnserv-interactive.obj: interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-interactive.obj -MD -MP -MF $(DEPDIR)/openvpnserv-interactive.Tpo -c -o openvpnserv-interactive.obj `if test -f 'interactive.c'; then $(CYGPATH_W) 'interactive.c'; else $(CYGPATH_W) '$(srcdir)/interactive.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-interactive.Tpo $(DEPDIR)/openvpnserv-interactive.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interactive.c' object='openvpnserv-interactive.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-interactive.obj `if test -f 'interactive.c'; then $(CYGPATH_W) 'interactive.c'; else $(CYGPATH_W) '$(srcdir)/interactive.c'; fi`
+
+openvpnserv-service.o: service.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-service.o -MD -MP -MF $(DEPDIR)/openvpnserv-service.Tpo -c -o openvpnserv-service.o `test -f 'service.c' || echo '$(srcdir)/'`service.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-service.Tpo $(DEPDIR)/openvpnserv-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='service.c' object='openvpnserv-service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-service.o `test -f 'service.c' || echo '$(srcdir)/'`service.c
+
+openvpnserv-service.obj: service.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-service.obj -MD -MP -MF $(DEPDIR)/openvpnserv-service.Tpo -c -o openvpnserv-service.obj `if test -f 'service.c'; then $(CYGPATH_W) 'service.c'; else $(CYGPATH_W) '$(srcdir)/service.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-service.Tpo $(DEPDIR)/openvpnserv-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='service.c' object='openvpnserv-service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-service.obj `if test -f 'service.c'; then $(CYGPATH_W) 'service.c'; else $(CYGPATH_W) '$(srcdir)/service.c'; fi`
+
+openvpnserv-validate.o: validate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-validate.o -MD -MP -MF $(DEPDIR)/openvpnserv-validate.Tpo -c -o openvpnserv-validate.o `test -f 'validate.c' || echo '$(srcdir)/'`validate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-validate.Tpo $(DEPDIR)/openvpnserv-validate.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='validate.c' object='openvpnserv-validate.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-validate.o `test -f 'validate.c' || echo '$(srcdir)/'`validate.c
+
+openvpnserv-validate.obj: validate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-validate.obj -MD -MP -MF $(DEPDIR)/openvpnserv-validate.Tpo -c -o openvpnserv-validate.obj `if test -f 'validate.c'; then $(CYGPATH_W) 'validate.c'; else $(CYGPATH_W) '$(srcdir)/validate.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-validate.Tpo $(DEPDIR)/openvpnserv-validate.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='validate.c' object='openvpnserv-validate.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-validate.obj `if test -f 'validate.c'; then $(CYGPATH_W) 'validate.c'; else $(CYGPATH_W) '$(srcdir)/validate.c'; fi`
+
+openvpnserv-block_dns.o: $(top_srcdir)/src/openvpn/block_dns.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-block_dns.o -MD -MP -MF $(DEPDIR)/openvpnserv-block_dns.Tpo -c -o openvpnserv-block_dns.o `test -f '$(top_srcdir)/src/openvpn/block_dns.c' || echo '$(srcdir)/'`$(top_srcdir)/src/openvpn/block_dns.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-block_dns.Tpo $(DEPDIR)/openvpnserv-block_dns.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/openvpn/block_dns.c' object='openvpnserv-block_dns.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-block_dns.o `test -f '$(top_srcdir)/src/openvpn/block_dns.c' || echo '$(srcdir)/'`$(top_srcdir)/src/openvpn/block_dns.c
+
+openvpnserv-block_dns.obj: $(top_srcdir)/src/openvpn/block_dns.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-block_dns.obj -MD -MP -MF $(DEPDIR)/openvpnserv-block_dns.Tpo -c -o openvpnserv-block_dns.obj `if test -f '$(top_srcdir)/src/openvpn/block_dns.c'; then $(CYGPATH_W) '$(top_srcdir)/src/openvpn/block_dns.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/openvpn/block_dns.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-block_dns.Tpo $(DEPDIR)/openvpnserv-block_dns.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/openvpn/block_dns.c' object='openvpnserv-block_dns.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-block_dns.obj `if test -f '$(top_srcdir)/src/openvpn/block_dns.c'; then $(CYGPATH_W) '$(top_srcdir)/src/openvpn/block_dns.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/openvpn/block_dns.c'; fi`
+
mostlyclean-libtool:
-rm -f *.lo
@@ -708,8 +817,6 @@ uninstall-am: uninstall-sbinPROGRAMS
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS
-.PRECIOUS: Makefile
-
.rc.lo:
$(LTRCCOMPILE) -i "$<" -o "$@"
diff --git a/src/openvpnserv/automatic.c b/src/openvpnserv/automatic.c
new file mode 100644
index 0000000..aa7618f
--- /dev/null
+++ b/src/openvpnserv/automatic.c
@@ -0,0 +1,415 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This program allows one or more OpenVPN processes to be started
+ * as a service. To build, you must get the service sample from the
+ * Platform SDK and replace Simple.c with this file.
+ *
+ * You should also apply service.patch to
+ * service.c and service.h from the Platform SDK service sample.
+ *
+ * This code is designed to be built with the mingw compiler.
+ */
+
+#include "service.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <process.h>
+
+/* bool definitions */
+#define bool int
+#define true 1
+#define false 0
+
+static SERVICE_STATUS_HANDLE service;
+static SERVICE_STATUS status;
+
+openvpn_service_t automatic_service = {
+ automatic,
+ TEXT(PACKAGE_NAME "ServiceLegacy"),
+ TEXT(PACKAGE_NAME " Legacy Service"),
+ TEXT(SERVICE_DEPENDENCIES),
+ SERVICE_DEMAND_START
+};
+
+struct security_attributes
+{
+ SECURITY_ATTRIBUTES sa;
+ SECURITY_DESCRIPTOR sd;
+};
+
+/*
+ * Which registry key in HKLM should
+ * we get config info from?
+ */
+#define REG_KEY "SOFTWARE\\" PACKAGE_NAME
+
+static HANDLE exit_event = NULL;
+
+/* clear an object */
+#define CLEAR(x) memset(&(x), 0, sizeof(x))
+
+
+bool
+init_security_attributes_allow_all (struct security_attributes *obj)
+{
+ CLEAR (*obj);
+
+ obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+ obj->sa.lpSecurityDescriptor = &obj->sd;
+ obj->sa.bInheritHandle = TRUE;
+ if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION))
+ return false;
+ if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE))
+ return false;
+ return true;
+}
+
+/*
+ * This event is initially created in the non-signaled
+ * state. It will transition to the signaled state when
+ * we have received a terminate signal from the Service
+ * Control Manager which will cause an asynchronous call
+ * of ServiceStop below.
+ */
+#define EXIT_EVENT_NAME TEXT(PACKAGE "_exit_1")
+
+HANDLE
+create_event (LPCTSTR name, bool allow_all, bool initial_state, bool manual_reset)
+{
+ if (allow_all)
+ {
+ struct security_attributes sa;
+ if (!init_security_attributes_allow_all (&sa))
+ return NULL;
+ return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name);
+ }
+ else
+ return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name);
+}
+
+void
+close_if_open (HANDLE h)
+{
+ if (h != NULL)
+ CloseHandle (h);
+}
+
+static bool
+match (const WIN32_FIND_DATA *find, LPCTSTR ext)
+{
+ int i;
+
+ if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ return false;
+
+ if (!_tcslen (ext))
+ return true;
+
+ i = _tcslen (find->cFileName) - _tcslen (ext) - 1;
+ if (i < 1)
+ return false;
+
+ return find->cFileName[i] == '.' && !_tcsicmp (find->cFileName + i + 1, ext);
+}
+
+/*
+ * Modify the extension on a filename.
+ */
+static bool
+modext (LPTSTR dest, int size, LPCTSTR src, LPCTSTR newext)
+{
+ int i;
+
+ if (size > 0 && (_tcslen (src) + 1) <= size)
+ {
+ _tcscpy (dest, src);
+ dest [size - 1] = TEXT('\0');
+ i = _tcslen (dest);
+ while (--i >= 0)
+ {
+ if (dest[i] == TEXT('\\'))
+ break;
+ if (dest[i] == TEXT('.'))
+ {
+ dest[i] = TEXT('\0');
+ break;
+ }
+ }
+ if (_tcslen (dest) + _tcslen(newext) + 2 <= size)
+ {
+ _tcscat (dest, TEXT("."));
+ _tcscat (dest, newext);
+ return true;
+ }
+ dest[0] = TEXT('\0');
+ }
+ return false;
+}
+
+static DWORD WINAPI
+ServiceCtrlAutomatic (DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
+{
+ SERVICE_STATUS *status = ctx;
+ switch (ctrl_code)
+ {
+ case SERVICE_CONTROL_STOP:
+ status->dwCurrentState = SERVICE_STOP_PENDING;
+ ReportStatusToSCMgr (service, status);
+ if (exit_event)
+ SetEvent (exit_event);
+ return NO_ERROR;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ return NO_ERROR;
+
+ default:
+ return ERROR_CALL_NOT_IMPLEMENTED;
+ }
+}
+
+
+VOID WINAPI
+ServiceStartAutomatic (DWORD dwArgc, LPTSTR *lpszArgv)
+{
+ DWORD error = NO_ERROR;
+ settings_t settings;
+
+ service = RegisterServiceCtrlHandlerEx (automatic_service.name, ServiceCtrlAutomatic, &status);
+ if (!service)
+ return;
+
+ status.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
+ status.dwCurrentState = SERVICE_START_PENDING;
+ status.dwServiceSpecificExitCode = NO_ERROR;
+ status.dwWin32ExitCode = NO_ERROR;
+ status.dwWaitHint = 3000;
+
+ if (!ReportStatusToSCMgr(service, &status))
+ {
+ MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #1 failed"));
+ goto finish;
+ }
+
+ /*
+ * Create our exit event
+ */
+ exit_event = create_event (EXIT_EVENT_NAME, false, false, true);
+ if (!exit_event)
+ {
+ MsgToEventLog (M_ERR, TEXT("CreateEvent failed"));
+ goto finish;
+ }
+
+ /*
+ * If exit event is already signaled, it means we were not
+ * shut down properly.
+ */
+ if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT)
+ {
+ MsgToEventLog (M_ERR, TEXT("Exit event is already signaled -- we were not shut down properly"));
+ goto finish;
+ }
+
+ if (!ReportStatusToSCMgr(service, &status))
+ {
+ MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #2 failed"));
+ goto finish;
+ }
+
+ /*
+ * Read info from registry in key HKLM\SOFTWARE\OpenVPN
+ */
+ error = GetOpenvpnSettings (&settings);
+ if (error != ERROR_SUCCESS)
+ goto finish;
+
+ /*
+ * Instantiate an OpenVPN process for each configuration
+ * file found.
+ */
+ {
+ WIN32_FIND_DATA find_obj;
+ HANDLE find_handle;
+ BOOL more_files;
+ TCHAR find_string[MAX_PATH];
+
+ openvpn_sntprintf (find_string, MAX_PATH, TEXT("%s\\*"), settings.config_dir);
+
+ find_handle = FindFirstFile (find_string, &find_obj);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ {
+ MsgToEventLog (M_ERR, TEXT("Cannot get configuration file list using: %s"), find_string);
+ goto finish;
+ }
+
+ /*
+ * Loop over each config file
+ */
+ do {
+ HANDLE log_handle = NULL;
+ STARTUPINFO start_info;
+ PROCESS_INFORMATION proc_info;
+ struct security_attributes sa;
+ TCHAR log_file[MAX_PATH];
+ TCHAR log_path[MAX_PATH];
+ TCHAR command_line[256];
+
+ CLEAR (start_info);
+ CLEAR (proc_info);
+ CLEAR (sa);
+
+ if (!ReportStatusToSCMgr(service, &status))
+ {
+ MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #3 failed"));
+ FindClose (find_handle);
+ goto finish;
+ }
+
+ /* does file have the correct type and extension? */
+ if (match (&find_obj, settings.ext_string))
+ {
+ /* get log file pathname */
+ if (!modext (log_file, _countof (log_file), find_obj.cFileName, TEXT("log")))
+ {
+ MsgToEventLog (M_ERR, TEXT("Cannot construct logfile name based on: %s"), find_obj.cFileName);
+ FindClose (find_handle);
+ goto finish;
+ }
+ openvpn_sntprintf (log_path, _countof (log_path),
+ TEXT("%s\\%s"), settings.log_dir, log_file);
+
+ /* construct command line */
+ openvpn_sntprintf (command_line, _countof (command_line), TEXT(PACKAGE " --service %s 1 --config \"%s\""),
+ EXIT_EVENT_NAME,
+ find_obj.cFileName);
+
+ /* Make security attributes struct for logfile handle so it can
+ be inherited. */
+ if (!init_security_attributes_allow_all (&sa))
+ {
+ error = MsgToEventLog (M_SYSERR, TEXT("InitializeSecurityDescriptor start_" PACKAGE " failed"));
+ goto finish;
+ }
+
+ /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */
+ log_handle = CreateFile (log_path,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ &sa.sa,
+ settings.append ? OPEN_ALWAYS : CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (log_handle == INVALID_HANDLE_VALUE)
+ {
+ error = MsgToEventLog (M_SYSERR, TEXT("Cannot open logfile: %s"), log_path);
+ FindClose (find_handle);
+ goto finish;
+ }
+
+ /* append to logfile? */
+ if (settings.append)
+ {
+ if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ {
+ error = MsgToEventLog (M_SYSERR, TEXT("Cannot seek to end of logfile: %s"), log_path);
+ FindClose (find_handle);
+ goto finish;
+ }
+ }
+
+ /* fill in STARTUPINFO struct */
+ GetStartupInfo(&start_info);
+ start_info.cb = sizeof(start_info);
+ start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = SW_HIDE;
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start_info.hStdOutput = start_info.hStdError = log_handle;
+
+ /* create an OpenVPN process for one config file */
+ if (!CreateProcess(settings.exe_path,
+ command_line,
+ NULL,
+ NULL,
+ TRUE,
+ settings.priority | CREATE_NEW_CONSOLE,
+ NULL,
+ settings.config_dir,
+ &start_info,
+ &proc_info))
+ {
+ error = MsgToEventLog (M_SYSERR, TEXT("CreateProcess failed, exe='%s' cmdline='%s' dir='%s'"),
+ settings.exe_path,
+ command_line,
+ settings.config_dir);
+
+ FindClose (find_handle);
+ CloseHandle (log_handle);
+ goto finish;
+ }
+
+ /* close unneeded handles */
+ Sleep (1000); /* try to prevent race if we close logfile
+ handle before child process DUPs it */
+ if (!CloseHandle (proc_info.hProcess)
+ || !CloseHandle (proc_info.hThread)
+ || !CloseHandle (log_handle))
+ {
+ error = MsgToEventLog (M_SYSERR, TEXT("CloseHandle failed"));
+ goto finish;
+ }
+ }
+
+ /* more files to process? */
+ more_files = FindNextFile (find_handle, &find_obj);
+
+ } while (more_files);
+
+ FindClose (find_handle);
+ }
+
+ /* we are now fully started */
+ status.dwCurrentState = SERVICE_RUNNING;
+ status.dwWaitHint = 0;
+ if (!ReportStatusToSCMgr(service, &status))
+ {
+ MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr SERVICE_RUNNING failed"));
+ goto finish;
+ }
+
+ /* wait for our shutdown signal */
+ if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0)
+ MsgToEventLog (M_ERR, TEXT("wait for shutdown signal failed"));
+
+finish:
+ if (exit_event)
+ CloseHandle (exit_event);
+
+ status.dwCurrentState = SERVICE_STOPPED;
+ status.dwWin32ExitCode = error;
+ ReportStatusToSCMgr (service, &status);
+}
diff --git a/src/openvpnserv/common.c b/src/openvpnserv/common.c
new file mode 100644
index 0000000..dba4724
--- /dev/null
+++ b/src/openvpnserv/common.c
@@ -0,0 +1,218 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2011 Heiko Hund <heiko.hund@sophos.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <service.h>
+#include <validate.h>
+/*
+ * These are necessary due to certain buggy implementations of (v)snprintf,
+ * that don't guarantee null termination for size > 0.
+ */
+int
+openvpn_vsntprintf (LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
+{
+ int len = -1;
+ if (size > 0)
+ {
+ len = _vsntprintf (str, size, format, arglist);
+ str[size - 1] = 0;
+ }
+ return (len >= 0 && len < size);
+}
+int
+openvpn_sntprintf (LPTSTR str, size_t size, LPCTSTR format, ...)
+{
+ va_list arglist;
+ int len = -1;
+ if (size > 0)
+ {
+ va_start (arglist, format);
+ len = openvpn_vsntprintf (str, size, format, arglist);
+ va_end (arglist);
+ }
+ return len;
+}
+
+#define REG_KEY TEXT("SOFTWARE\\" PACKAGE_NAME)
+
+static DWORD
+GetRegString (HKEY key, LPCTSTR value, LPTSTR data, DWORD size)
+{
+ DWORD type;
+ LONG status = RegQueryValueEx (key, value, NULL, &type, (LPBYTE) data, &size);
+
+ if (status == ERROR_SUCCESS && type != REG_SZ)
+ status = ERROR_DATATYPE_MISMATCH;
+
+ if (status != ERROR_SUCCESS)
+ {
+ SetLastError (status);
+ return MsgToEventLog (M_SYSERR, TEXT("Error querying registry value: HKLM\\%s\\%s"), REG_KEY, value);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+
+DWORD
+GetOpenvpnSettings (settings_t *s)
+{
+ TCHAR priority[64];
+ TCHAR append[2];
+ DWORD error;
+ HKEY key;
+
+ LONG status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_KEY, 0, KEY_READ, &key);
+ if (status != ERROR_SUCCESS)
+ {
+ SetLastError (status);
+ return MsgToEventLog (M_SYSERR, TEXT("Could not open Registry key HKLM\\%s not found"), REG_KEY);
+ }
+
+ error = GetRegString (key, TEXT("exe_path"), s->exe_path, sizeof (s->exe_path));
+ if (error != ERROR_SUCCESS)
+ goto out;
+
+ error = GetRegString (key, TEXT("config_dir"), s->config_dir, sizeof (s->config_dir));
+ if (error != ERROR_SUCCESS)
+ goto out;
+
+ error = GetRegString (key, TEXT("config_ext"), s->ext_string, sizeof (s->ext_string));
+ if (error != ERROR_SUCCESS)
+ goto out;
+
+ error = GetRegString (key, TEXT("log_dir"), s->log_dir, sizeof (s->log_dir));
+ if (error != ERROR_SUCCESS)
+ goto out;
+
+ error = GetRegString (key, TEXT("priority"), priority, sizeof (priority));
+ if (error != ERROR_SUCCESS)
+ goto out;
+
+ error = GetRegString (key, TEXT("log_append"), append, sizeof (append));
+ if (error != ERROR_SUCCESS)
+ goto out;
+
+ /* read if present, else use default */
+ error = GetRegString (key, TEXT("ovpn_admin_group"), s->ovpn_admin_group, sizeof (s->ovpn_admin_group));
+ if (error != ERROR_SUCCESS)
+ {
+ openvpn_sntprintf(s->ovpn_admin_group, _countof(s->ovpn_admin_group), OVPN_ADMIN_GROUP);
+ error = 0; /* this error is not fatal */
+ }
+ /* set process priority */
+ if (!_tcsicmp (priority, TEXT("IDLE_PRIORITY_CLASS")))
+ s->priority = IDLE_PRIORITY_CLASS;
+ else if (!_tcsicmp (priority, TEXT("BELOW_NORMAL_PRIORITY_CLASS")))
+ s->priority = BELOW_NORMAL_PRIORITY_CLASS;
+ else if (!_tcsicmp (priority, TEXT("NORMAL_PRIORITY_CLASS")))
+ s->priority = NORMAL_PRIORITY_CLASS;
+ else if (!_tcsicmp (priority, TEXT("ABOVE_NORMAL_PRIORITY_CLASS")))
+ s->priority = ABOVE_NORMAL_PRIORITY_CLASS;
+ else if (!_tcsicmp (priority, TEXT("HIGH_PRIORITY_CLASS")))
+ s->priority = HIGH_PRIORITY_CLASS;
+ else
+ {
+ SetLastError (ERROR_INVALID_DATA);
+ error = MsgToEventLog (M_SYSERR, TEXT("Unknown priority name: %s"), priority);
+ goto out;
+ }
+
+ /* set log file append/truncate flag */
+ if (append[0] == TEXT('0'))
+ s->append = FALSE;
+ else if (append[0] == TEXT('1'))
+ s->append = TRUE;
+ else
+ {
+ SetLastError (ERROR_INVALID_DATA);
+ error = MsgToEventLog (M_ERR, TEXT("Log file append flag (given as '%s') must be '0' or '1'"), append);
+ goto out;
+ }
+
+out:
+ RegCloseKey (key);
+ return error;
+}
+
+
+LPCTSTR
+GetLastErrorText ()
+{
+ static TCHAR buf[256];
+ DWORD len;
+ LPTSTR tmp = NULL;
+
+ len = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL);
+
+ if (len == 0 || (long) _countof (buf) < (long) len + 14)
+ buf[0] = TEXT('\0');
+ else
+ {
+ tmp[_tcslen (tmp) - 2] = TEXT('\0'); /* remove CR/LF characters */
+ openvpn_sntprintf (buf, _countof (buf), TEXT("%s (0x%x)"), tmp, GetLastError());
+ }
+
+ if (tmp)
+ LocalFree (tmp);
+
+ return buf;
+}
+
+
+DWORD
+MsgToEventLog (DWORD flags, LPCTSTR format, ...)
+{
+ HANDLE hEventSource;
+ TCHAR msg[2][256];
+ DWORD error = 0;
+ LPCTSTR err_msg = TEXT("");
+ va_list arglist;
+
+ if (flags & MSG_FLAGS_SYS_CODE)
+ {
+ error = GetLastError ();
+ err_msg = GetLastErrorText ();
+ }
+
+ hEventSource = RegisterEventSource (NULL, APPNAME);
+ if (hEventSource != NULL)
+ {
+ openvpn_sntprintf (msg[0], _countof (msg[0]),
+ TEXT("%s%s: %s"), APPNAME,
+ (flags & MSG_FLAGS_ERROR) ? TEXT(" error") : TEXT(""), err_msg);
+
+ va_start (arglist, format);
+ openvpn_vsntprintf (msg[1], _countof (msg[1]), format, arglist);
+ va_end (arglist);
+
+ const TCHAR *mesg[] = { msg[0], msg[1] };
+ ReportEvent (hEventSource, flags & MSG_FLAGS_ERROR ?
+ EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
+ 0, 0, NULL, 2, 0, mesg, NULL);
+ DeregisterEventSource (hEventSource);
+ }
+
+ return error;
+}
diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c
new file mode 100644
index 0000000..ffaa171
--- /dev/null
+++ b/src/openvpnserv/interactive.c
@@ -0,0 +1,1652 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2012 Heiko Hund <heiko.hund@sophos.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "service.h"
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+#include <userenv.h>
+#include <accctrl.h>
+#include <aclapi.h>
+#include <stdio.h>
+#include <sddl.h>
+#include <shellapi.h>
+
+#include "openvpn-msg.h"
+#include "validate.h"
+#include "block_dns.h"
+
+#define IO_TIMEOUT 2000 /*ms*/
+
+#define ERROR_OPENVPN_STARTUP 0x20000000
+#define ERROR_STARTUP_DATA 0x20000001
+#define ERROR_MESSAGE_DATA 0x20000002
+#define ERROR_MESSAGE_TYPE 0x20000003
+
+static SERVICE_STATUS_HANDLE service;
+static SERVICE_STATUS status;
+static HANDLE exit_event = NULL;
+static settings_t settings;
+static HANDLE rdns_semaphore = NULL;
+#define RDNS_TIMEOUT 600 /* seconds to wait for the semaphore */
+
+
+openvpn_service_t interactive_service = {
+ interactive,
+ TEXT(PACKAGE_NAME "ServiceInteractive"),
+ TEXT(PACKAGE_NAME " Interactive Service"),
+ TEXT(SERVICE_DEPENDENCIES),
+ SERVICE_AUTO_START
+};
+
+
+typedef struct {
+ WCHAR *directory;
+ WCHAR *options;
+ WCHAR *std_input;
+} STARTUP_DATA;
+
+
+/* Datatype for linked lists */
+typedef struct _list_item {
+ struct _list_item *next;
+ LPVOID data;
+} list_item_t;
+
+
+/* Datatypes for undo information */
+typedef enum {
+ address,
+ route,
+ block_dns,
+ _undo_type_max
+} undo_type_t;
+typedef list_item_t* undo_lists_t[_undo_type_max];
+
+
+static DWORD
+AddListItem (list_item_t **pfirst, LPVOID data)
+{
+ list_item_t *new_item = malloc (sizeof (list_item_t));
+ if (new_item == NULL)
+ return ERROR_OUTOFMEMORY;
+
+ new_item->next = *pfirst;
+ new_item->data = data;
+
+ *pfirst = new_item;
+ return NO_ERROR;
+}
+
+typedef BOOL (*match_fn_t) (LPVOID item, LPVOID ctx);
+
+static LPVOID
+RemoveListItem (list_item_t **pfirst, match_fn_t match, LPVOID ctx)
+{
+ LPVOID data = NULL;
+ list_item_t **pnext;
+
+ for (pnext = pfirst; *pnext; pnext = &(*pnext)->next)
+ {
+ list_item_t *item = *pnext;
+ if (!match (item->data, ctx))
+ continue;
+
+ /* Found item, remove from the list and free memory */
+ *pnext = item->next;
+ data = item->data;
+ free (item);
+ break;
+ }
+ return data;
+}
+
+
+static HANDLE
+CloseHandleEx (LPHANDLE handle)
+{
+ if (handle && *handle && *handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (*handle);
+ *handle = INVALID_HANDLE_VALUE;
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+
+static HANDLE
+InitOverlapped (LPOVERLAPPED overlapped)
+{
+ ZeroMemory (overlapped, sizeof (OVERLAPPED));
+ overlapped->hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+ return overlapped->hEvent;
+}
+
+
+static BOOL
+ResetOverlapped (LPOVERLAPPED overlapped)
+{
+ HANDLE io_event = overlapped->hEvent;
+ if (!ResetEvent (io_event))
+ return FALSE;
+ ZeroMemory (overlapped, sizeof (OVERLAPPED));
+ overlapped->hEvent = io_event;
+ return TRUE;
+}
+
+
+typedef enum {
+ peek,
+ read,
+ write
+} async_op_t;
+
+static DWORD
+AsyncPipeOp (async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
+{
+ int i;
+ BOOL success;
+ HANDLE io_event;
+ DWORD res, bytes = 0;
+ OVERLAPPED overlapped;
+ LPHANDLE handles = NULL;
+
+ io_event = InitOverlapped (&overlapped);
+ if (!io_event)
+ goto out;
+
+ handles = malloc ((count + 1) * sizeof (HANDLE));
+ if (!handles)
+ goto out;
+
+ if (op == write)
+ success = WriteFile (pipe, buffer, size, NULL, &overlapped);
+ else
+ success = ReadFile (pipe, buffer, size, NULL, &overlapped);
+ if (!success && GetLastError () != ERROR_IO_PENDING && GetLastError () != ERROR_MORE_DATA)
+ goto out;
+
+ handles[0] = io_event;
+ for (i = 0; i < count; i++)
+ handles[i + 1] = events[i];
+
+ res = WaitForMultipleObjects (count + 1, handles, FALSE,
+ op == peek ? INFINITE : IO_TIMEOUT);
+ if (res != WAIT_OBJECT_0)
+ {
+ CancelIo (pipe);
+ goto out;
+ }
+
+ if (op == peek)
+ PeekNamedPipe (pipe, NULL, 0, NULL, &bytes, NULL);
+ else
+ GetOverlappedResult (pipe, &overlapped, &bytes, TRUE);
+
+out:
+ CloseHandleEx (&io_event);
+ free (handles);
+ return bytes;
+}
+
+static DWORD
+PeekNamedPipeAsync (HANDLE pipe, DWORD count, LPHANDLE events)
+{
+ return AsyncPipeOp (peek, pipe, NULL, 0, count, events);
+}
+
+static DWORD
+ReadPipeAsync (HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
+{
+ return AsyncPipeOp (read, pipe, buffer, size, count, events);
+}
+
+static DWORD
+WritePipeAsync (HANDLE pipe, LPVOID data, DWORD size, DWORD count, LPHANDLE events)
+{
+ return AsyncPipeOp (write, pipe, data, size, count, events);
+}
+
+static VOID
+ReturnProcessId (HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
+{
+ const WCHAR msg[] = L"Process ID";
+ WCHAR buf[22 + _countof(msg)]; /* 10 chars each for error and PID and 2 for line breaks */
+
+ /*
+ * Same format as error messages (3 line string) with error = 0 in
+ * 0x%08x format, PID on line 2 and a description "Process ID" on line 3
+ */
+ _snwprintf (buf, _countof(buf), L"0x%08x\n0x%08x\n%s", 0, pid, msg);
+ buf[_countof(buf) - 1] = '\0';
+
+ WritePipeAsync (pipe, buf, wcslen (buf) * 2, count, events);
+}
+
+static VOID
+ReturnError (HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events)
+{
+ DWORD result_len;
+ LPWSTR result = L"0xffffffff\nFormatMessage failed\nCould not return result";
+ DWORD_PTR args[] = {
+ (DWORD_PTR) error,
+ (DWORD_PTR) func,
+ (DWORD_PTR) ""
+ };
+
+ if (error != ERROR_OPENVPN_STARTUP)
+ {
+ FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, error, 0, (LPWSTR) &args[2], 0, NULL);
+ }
+
+ result_len = FormatMessageW (FORMAT_MESSAGE_FROM_STRING |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ L"0x%1!08x!\n%2!s!\n%3!s!", 0, 0,
+ (LPWSTR) &result, 0, (va_list*) args);
+
+ WritePipeAsync (pipe, result, wcslen (result) * 2, count, events);
+#ifdef UNICODE
+ MsgToEventLog (MSG_FLAGS_ERROR, result);
+#else
+ MsgToEventLog (MSG_FLAGS_ERROR, "%S", result);
+#endif
+
+ if (error != ERROR_OPENVPN_STARTUP)
+ LocalFree ((LPVOID) args[2]);
+ if (result_len)
+ LocalFree (result);
+}
+
+
+static VOID
+ReturnLastError (HANDLE pipe, LPCWSTR func)
+{
+ ReturnError (pipe, GetLastError (), func, 1, &exit_event);
+}
+
+
+static VOID
+ReturnOpenvpnOutput (HANDLE pipe, HANDLE ovpn_output, DWORD count, LPHANDLE events)
+{
+ WCHAR *wide_output = NULL;
+ CHAR output[512];
+ DWORD size;
+
+ ReadFile (ovpn_output, output, sizeof (output), &size, NULL);
+ if (size == 0)
+ return;
+
+ wide_output = malloc ((size) * sizeof (WCHAR));
+ if (wide_output)
+ {
+ MultiByteToWideChar (CP_UTF8, 0, output, size, wide_output, size);
+ wide_output[size - 1] = 0;
+ }
+
+ ReturnError (pipe, ERROR_OPENVPN_STARTUP, wide_output, count, events);
+ free (wide_output);
+}
+
+/*
+ * Validate options against a white list. Also check the config_file is
+ * inside the config_dir. The white list is defined in validate.c
+ * Returns true on success
+ */
+static BOOL
+ValidateOptions (HANDLE pipe, const WCHAR *workdir, const WCHAR *options)
+{
+ WCHAR **argv;
+ int argc;
+ WCHAR buf[256];
+ BOOL ret = FALSE;
+ int i;
+ const WCHAR *msg1 = L"You have specified a config file location (%s relative to %s)"
+ " that requires admin approval. This error may be avoided"
+ " by adding your account to the \"%s\" group";
+
+ const WCHAR *msg2 = L"You have specified an option (%s) that may be used"
+ " only with admin approval. This error may be avoided"
+ " by adding your account to the \"%s\" group";
+
+ argv = CommandLineToArgvW (options, &argc);
+
+ if (!argv)
+ {
+ ReturnLastError (pipe, L"CommandLineToArgvW");
+ ReturnError (pipe, ERROR_STARTUP_DATA, L"Cannot validate options", 1, &exit_event);
+ goto out;
+ }
+
+ /* Note: argv[0] is the first option */
+ if (argc < 1) /* no options */
+ {
+ ret = TRUE;
+ goto out;
+ }
+
+ /*
+ * If only one argument, it is the config file
+ */
+ if (argc == 1)
+ {
+ WCHAR *argv_tmp[2] = { L"--config", argv[0] };
+
+ if (!CheckOption (workdir, 2, argv_tmp, &settings))
+ {
+ snwprintf (buf, _countof(buf), msg1, argv[0], workdir,
+ settings.ovpn_admin_group);
+ buf[_countof(buf) - 1] = L'\0';
+ ReturnError (pipe, ERROR_STARTUP_DATA, buf, 1, &exit_event);
+ }
+ goto out;
+ }
+
+ for (i = 0; i < argc; ++i)
+ {
+ if (!IsOption(argv[i]))
+ continue;
+
+ if (!CheckOption (workdir, argc-i, &argv[i], &settings))
+ {
+ if (wcscmp(L"--config", argv[i]) == 0 && argc-i > 1)
+ {
+ snwprintf (buf, _countof(buf), msg1, argv[i+1], workdir,
+ settings.ovpn_admin_group);
+ buf[_countof(buf) - 1] = L'\0';
+ ReturnError (pipe, ERROR_STARTUP_DATA, buf, 1, &exit_event);
+ }
+ else
+ {
+ snwprintf (buf, _countof(buf), msg2, argv[i],
+ settings.ovpn_admin_group);
+ buf[_countof(buf) - 1] = L'\0';
+ ReturnError (pipe, ERROR_STARTUP_DATA, buf, 1, &exit_event);
+ }
+ goto out;
+ }
+ }
+
+ /* all options passed */
+ ret = TRUE;
+
+out:
+ if (argv)
+ LocalFree (argv);
+ return ret;
+}
+
+static BOOL
+GetStartupData (HANDLE pipe, STARTUP_DATA *sud)
+{
+ size_t len;
+ BOOL ret = FALSE;
+ WCHAR *data = NULL;
+ DWORD size, bytes, read;
+
+ bytes = PeekNamedPipeAsync (pipe, 1, &exit_event);
+ if (bytes == 0)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("PeekNamedPipeAsync failed"));
+ ReturnLastError (pipe, L"PeekNamedPipeAsync");
+ goto out;
+ }
+
+ size = bytes / sizeof (*data);
+ data = malloc (bytes);
+ if (data == NULL)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("malloc failed"));
+ ReturnLastError (pipe, L"malloc");
+ goto out;
+ }
+
+ read = ReadPipeAsync (pipe, data, bytes, 1, &exit_event);
+ if (bytes != read)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("ReadPipeAsync failed"));
+ ReturnLastError (pipe, L"ReadPipeAsync");
+ goto out;
+ }
+
+ if (data[size - 1] != 0)
+ {
+ MsgToEventLog (M_ERR, TEXT("Startup data is not NULL terminated"));
+ ReturnError (pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
+ goto out;
+ }
+
+ sud->directory = data;
+ len = wcslen (sud->directory) + 1;
+ size -= len;
+ if (size <= 0)
+ {
+ MsgToEventLog (M_ERR, TEXT("Startup data ends at working directory"));
+ ReturnError (pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
+ goto out;
+ }
+
+ sud->options = sud->directory + len;
+ len = wcslen (sud->options) + 1;
+ size -= len;
+ if (size <= 0)
+ {
+ MsgToEventLog (M_ERR, TEXT("Startup data ends at command line options"));
+ ReturnError (pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
+ goto out;
+ }
+
+ sud->std_input = sud->options + len;
+ data = NULL; /* don't free data */
+ ret = TRUE;
+
+out:
+ free (data);
+ return ret;
+}
+
+
+static VOID
+FreeStartupData (STARTUP_DATA *sud)
+{
+ free (sud->directory);
+}
+
+
+static SOCKADDR_INET
+sockaddr_inet (short family, inet_address_t *addr)
+{
+ SOCKADDR_INET sa_inet;
+ ZeroMemory (&sa_inet, sizeof (sa_inet));
+ sa_inet.si_family = family;
+ if (family == AF_INET)
+ sa_inet.Ipv4.sin_addr = addr->ipv4;
+ else if (family == AF_INET6)
+ sa_inet.Ipv6.sin6_addr = addr->ipv6;
+ return sa_inet;
+}
+
+static DWORD
+InterfaceLuid (const char *iface_name, PNET_LUID luid)
+{
+ NETIO_STATUS status;
+ LPWSTR wide_name;
+ int n;
+
+ typedef NETIO_STATUS WINAPI (*ConvertInterfaceAliasToLuidFn) (LPCWSTR, PNET_LUID);
+ static ConvertInterfaceAliasToLuidFn ConvertInterfaceAliasToLuid = NULL;
+ if (!ConvertInterfaceAliasToLuid)
+ {
+ HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll"));
+ if (iphlpapi == NULL)
+ return GetLastError ();
+
+ ConvertInterfaceAliasToLuid = (ConvertInterfaceAliasToLuidFn) GetProcAddress (iphlpapi, "ConvertInterfaceAliasToLuid");
+ if (!ConvertInterfaceAliasToLuid)
+ return GetLastError ();
+ }
+
+ n = MultiByteToWideChar (CP_UTF8, 0, iface_name, -1, NULL, 0);
+ wide_name = malloc (n * sizeof (WCHAR));
+ MultiByteToWideChar (CP_UTF8, 0, iface_name, -1, wide_name, n);
+ status = ConvertInterfaceAliasToLuid (wide_name, luid);
+ free (wide_name);
+
+ return status;
+}
+
+static BOOL
+CmpAddress (LPVOID item, LPVOID address)
+{
+ return memcmp (item, address, sizeof (MIB_UNICASTIPADDRESS_ROW)) == 0 ? TRUE : FALSE;
+}
+
+static DWORD
+DeleteAddress (PMIB_UNICASTIPADDRESS_ROW addr_row)
+{
+ typedef NETIOAPI_API (*DeleteUnicastIpAddressEntryFn) (const PMIB_UNICASTIPADDRESS_ROW);
+ static DeleteUnicastIpAddressEntryFn DeleteUnicastIpAddressEntry = NULL;
+
+ if (!DeleteUnicastIpAddressEntry)
+ {
+ HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll"));
+ if (iphlpapi == NULL)
+ return GetLastError ();
+
+ DeleteUnicastIpAddressEntry = (DeleteUnicastIpAddressEntryFn) GetProcAddress (iphlpapi, "DeleteUnicastIpAddressEntry");
+ if (!DeleteUnicastIpAddressEntry)
+ return GetLastError ();
+ }
+
+ return DeleteUnicastIpAddressEntry (addr_row);
+}
+
+static DWORD
+HandleAddressMessage (address_message_t *msg, undo_lists_t *lists)
+{
+ DWORD err;
+ PMIB_UNICASTIPADDRESS_ROW addr_row;
+ BOOL add = msg->header.type == msg_add_address;
+
+ typedef NETIOAPI_API (*CreateUnicastIpAddressEntryFn) (const PMIB_UNICASTIPADDRESS_ROW);
+ typedef NETIOAPI_API (*InitializeUnicastIpAddressEntryFn) (PMIB_UNICASTIPADDRESS_ROW);
+ static CreateUnicastIpAddressEntryFn CreateUnicastIpAddressEntry = NULL;
+ static InitializeUnicastIpAddressEntryFn InitializeUnicastIpAddressEntry = NULL;
+
+ if (!CreateUnicastIpAddressEntry || !InitializeUnicastIpAddressEntry)
+ {
+ HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll"));
+ if (iphlpapi == NULL)
+ return GetLastError ();
+
+ CreateUnicastIpAddressEntry = (CreateUnicastIpAddressEntryFn) GetProcAddress (iphlpapi, "CreateUnicastIpAddressEntry");
+ if (!CreateUnicastIpAddressEntry)
+ return GetLastError ();
+
+ InitializeUnicastIpAddressEntry = (InitializeUnicastIpAddressEntryFn) GetProcAddress (iphlpapi, "InitializeUnicastIpAddressEntry");
+ if (!InitializeUnicastIpAddressEntry)
+ return GetLastError ();
+ }
+
+ addr_row = malloc (sizeof (*addr_row));
+ if (addr_row == NULL)
+ return ERROR_OUTOFMEMORY;
+
+ InitializeUnicastIpAddressEntry (addr_row);
+ addr_row->Address = sockaddr_inet (msg->family, &msg->address);
+ addr_row->OnLinkPrefixLength = (UINT8) msg->prefix_len;
+
+ if (msg->iface.index != -1)
+ {
+ addr_row->InterfaceIndex = msg->iface.index;
+ }
+ else
+ {
+ NET_LUID luid;
+ err = InterfaceLuid (msg->iface.name, &luid);
+ if (err)
+ goto out;
+ addr_row->InterfaceLuid = luid;
+ }
+
+ if (add)
+ {
+ err = CreateUnicastIpAddressEntry (addr_row);
+ if (err)
+ goto out;
+
+ err = AddListItem (&(*lists)[address], addr_row);
+ if (err)
+ DeleteAddress (addr_row);
+ }
+ else
+ {
+ err = DeleteAddress (addr_row);
+ if (err)
+ goto out;
+
+ free (RemoveListItem (&(*lists)[address], CmpAddress, addr_row));
+ }
+
+out:
+ if (!add || err)
+ free (addr_row);
+
+ return err;
+}
+
+static BOOL
+CmpRoute (LPVOID item, LPVOID route)
+{
+ return memcmp (item, route, sizeof (MIB_IPFORWARD_ROW2)) == 0 ? TRUE : FALSE;
+}
+
+static DWORD
+DeleteRoute (PMIB_IPFORWARD_ROW2 fwd_row)
+{
+ typedef NETIOAPI_API (*DeleteIpForwardEntry2Fn) (PMIB_IPFORWARD_ROW2);
+ static DeleteIpForwardEntry2Fn DeleteIpForwardEntry2 = NULL;
+
+ if (!DeleteIpForwardEntry2)
+ {
+ HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll"));
+ if (iphlpapi == NULL)
+ return GetLastError ();
+
+ DeleteIpForwardEntry2 = (DeleteIpForwardEntry2Fn) GetProcAddress (iphlpapi, "DeleteIpForwardEntry2");
+ if (!DeleteIpForwardEntry2)
+ return GetLastError ();
+ }
+
+ return DeleteIpForwardEntry2 (fwd_row);
+}
+
+static DWORD
+HandleRouteMessage (route_message_t *msg, undo_lists_t *lists)
+{
+ DWORD err;
+ PMIB_IPFORWARD_ROW2 fwd_row;
+ BOOL add = msg->header.type == msg_add_route;
+
+ typedef NETIOAPI_API (*CreateIpForwardEntry2Fn) (PMIB_IPFORWARD_ROW2);
+ static CreateIpForwardEntry2Fn CreateIpForwardEntry2 = NULL;
+
+ if (!CreateIpForwardEntry2)
+ {
+ HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll"));
+ if (iphlpapi == NULL)
+ return GetLastError ();
+
+ CreateIpForwardEntry2 = (CreateIpForwardEntry2Fn) GetProcAddress (iphlpapi, "CreateIpForwardEntry2");
+ if (!CreateIpForwardEntry2)
+ return GetLastError ();
+ }
+
+ fwd_row = malloc (sizeof (*fwd_row));
+ if (fwd_row == NULL)
+ return ERROR_OUTOFMEMORY;
+
+ ZeroMemory (fwd_row, sizeof (*fwd_row));
+ fwd_row->ValidLifetime = 0xffffffff;
+ fwd_row->PreferredLifetime = 0xffffffff;
+ fwd_row->Protocol = MIB_IPPROTO_NETMGMT;
+ fwd_row->Metric = msg->metric;
+ fwd_row->DestinationPrefix.Prefix = sockaddr_inet (msg->family, &msg->prefix);
+ fwd_row->DestinationPrefix.PrefixLength = (UINT8) msg->prefix_len;
+ fwd_row->NextHop = sockaddr_inet (msg->family, &msg->gateway);
+
+ if (msg->iface.index != -1)
+ {
+ fwd_row->InterfaceIndex = msg->iface.index;
+ }
+ else if (strlen (msg->iface.name))
+ {
+ NET_LUID luid;
+ err = InterfaceLuid (msg->iface.name, &luid);
+ if (err)
+ goto out;
+ fwd_row->InterfaceLuid = luid;
+ }
+
+ if (add)
+ {
+ err = CreateIpForwardEntry2 (fwd_row);
+ if (err)
+ goto out;
+
+ err = AddListItem (&(*lists)[route], fwd_row);
+ if (err)
+ DeleteRoute (fwd_row);
+ }
+ else
+ {
+ err = DeleteRoute (fwd_row);
+ if (err)
+ goto out;
+
+ free (RemoveListItem (&(*lists)[route], CmpRoute, fwd_row));
+ }
+
+out:
+ if (!add || err)
+ free (fwd_row);
+
+ return err;
+}
+
+
+static DWORD
+HandleFlushNeighborsMessage (flush_neighbors_message_t *msg)
+{
+ typedef NETIOAPI_API (*FlushIpNetTable2Fn) (ADDRESS_FAMILY, NET_IFINDEX);
+ static FlushIpNetTable2Fn flush_fn = NULL;
+
+ if (msg->family == AF_INET)
+ return FlushIpNetTable (msg->iface.index);
+
+ if (!flush_fn)
+ {
+ HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll"));
+ if (iphlpapi == NULL)
+ return GetLastError ();
+
+ flush_fn = (FlushIpNetTable2Fn) GetProcAddress (iphlpapi, "FlushIpNetTable2");
+ if (!flush_fn)
+ {
+ if (GetLastError () == ERROR_PROC_NOT_FOUND)
+ return WSAEPFNOSUPPORT;
+ else
+ return GetLastError ();
+ }
+ }
+ return flush_fn (msg->family, msg->iface.index);
+}
+
+static void
+BlockDNSErrHandler (DWORD err, const char *msg)
+{
+ TCHAR buf[256];
+ LPCTSTR err_str;
+
+ if (!err) return;
+
+ err_str = TEXT("Unknown Win32 Error");
+
+ if (FormatMessage (FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ NULL, err, 0, buf, sizeof (buf), NULL))
+ {
+ err_str = buf;
+ }
+
+#ifdef UNICODE
+ MsgToEventLog (M_ERR, L"%S (status = %lu): %s", msg, err, err_str);
+#else
+ MsgToEventLog (M_ERR, "%s (status = %lu): %s", msg, err, err_str);
+#endif
+
+}
+
+/* Use an always-true match_fn to get the head of the list */
+static BOOL
+CmpEngine (LPVOID item, LPVOID any)
+{
+ return TRUE;
+}
+
+static DWORD
+HandleBlockDNSMessage (const block_dns_message_t *msg, undo_lists_t *lists)
+{
+ DWORD err = 0;
+ HANDLE engine = NULL;
+ LPCWSTR exe_path;
+
+#ifdef UNICODE
+ exe_path = settings.exe_path;
+#else
+ WCHAR wide_path[MAX_PATH];
+ MultiByteToWideChar (CP_UTF8, 0, settings.exe_path, MAX_PATH, wide_path, MAX_PATH);
+ exe_path = wide_path;
+#endif
+
+ if (msg->header.type == msg_add_block_dns)
+ {
+ err = add_block_dns_filters (&engine, msg->iface.index, exe_path, BlockDNSErrHandler);
+ if (!err)
+ err = AddListItem (&(*lists)[block_dns], engine);
+ }
+ else
+ {
+ engine = RemoveListItem (&(*lists)[block_dns], CmpEngine, NULL);
+ if (engine)
+ {
+ err = delete_block_dns_filters (engine);
+ engine = NULL;
+ }
+ else
+ MsgToEventLog (M_ERR, TEXT("No previous block DNS filters to delete"));
+ }
+
+ if (err && engine)
+ {
+ delete_block_dns_filters (engine);
+ }
+
+ return err;
+}
+
+/*
+ * Execute a command and return its exit code. If timeout > 0, terminate
+ * the process if still running after timeout milliseconds. In that case
+ * the return value is the windows error code WAIT_TIMEOUT = 0x102
+ */
+static DWORD
+ExecCommand (const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
+{
+ DWORD exit_code;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ DWORD proc_flags = CREATE_NO_WINDOW|CREATE_UNICODE_ENVIRONMENT;
+ WCHAR *cmdline_dup = NULL;
+
+ ZeroMemory (&si, sizeof(si));
+ ZeroMemory (&pi, sizeof(pi));
+
+ si.cb = sizeof(si);
+
+ /* CreateProcess needs a modifiable cmdline: make a copy */
+ cmdline_dup = wcsdup (cmdline);
+ if ( cmdline_dup && CreateProcessW (argv0, cmdline_dup, NULL, NULL, FALSE,
+ proc_flags, NULL, NULL, &si, &pi) )
+ {
+ WaitForSingleObject (pi.hProcess, timeout ? timeout : INFINITE);
+ if (!GetExitCodeProcess (pi.hProcess, &exit_code))
+ {
+ MsgToEventLog (M_SYSERR, TEXT("ExecCommand: Error getting exit_code:"));
+ exit_code = GetLastError();
+ }
+ else if (exit_code == STILL_ACTIVE)
+ {
+ exit_code = WAIT_TIMEOUT; /* Windows error code 0x102 */
+
+ /* kill without impunity */
+ TerminateProcess (pi.hProcess, exit_code);
+ MsgToEventLog (M_ERR, TEXT("ExecCommand: \"%s %s\" killed after timeout"),
+ argv0, cmdline);
+ }
+ else if (exit_code)
+ MsgToEventLog (M_ERR, TEXT("ExecCommand: \"%s %s\" exited with status = %lu"),
+ argv0, cmdline, exit_code);
+ else
+ MsgToEventLog (M_INFO, TEXT("ExecCommand: \"%s %s\" completed"), argv0, cmdline);
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ else
+ {
+ exit_code = GetLastError();
+ MsgToEventLog (M_SYSERR, TEXT("ExecCommand: could not run \"%s %s\" :"),
+ argv0, cmdline);
+ }
+
+ free (cmdline_dup);
+ return exit_code;
+}
+
+/*
+ * Entry point for register-dns thread.
+ */
+static DWORD WINAPI
+RegisterDNS (LPVOID unused)
+{
+ DWORD err;
+ DWORD i;
+ WCHAR sys_path[MAX_PATH];
+ DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
+
+ /* default paths of net and ipconfig commands */
+ WCHAR net[MAX_PATH] = L"C:\\Windows\\system32\\net.exe";
+ WCHAR ipcfg[MAX_PATH] = L"C:\\Windows\\system32\\ipconfig.exe";
+
+ struct
+ {
+ WCHAR *argv0;
+ WCHAR *cmdline;
+ DWORD timeout;
+ } cmds [] = {
+ { net, L"net stop dnscache", timeout },
+ { net, L"net start dnscache", timeout },
+ { ipcfg, L"ipconfig /flushdns", timeout },
+ { ipcfg, L"ipconfig /registerdns", timeout },
+ };
+ int ncmds = sizeof (cmds) / sizeof (cmds[0]);
+
+ HANDLE wait_handles[2] = {rdns_semaphore, exit_event};
+
+ if(GetSystemDirectory(sys_path, MAX_PATH))
+ {
+ _snwprintf (net, MAX_PATH, L"%s\\%s", sys_path, L"net.exe");
+ net[MAX_PATH-1] = L'\0';
+
+ _snwprintf (ipcfg, MAX_PATH, L"%s\\%s", sys_path, L"ipconfig.exe");
+ ipcfg[MAX_PATH-1] = L'\0';
+ }
+
+ if (WaitForMultipleObjects (2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
+ {
+ /* Semaphore locked */
+ for (i = 0; i < ncmds; ++i)
+ {
+ ExecCommand (cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
+ }
+ err = 0;
+ if ( !ReleaseSemaphore (rdns_semaphore, 1, NULL) )
+ err = MsgToEventLog (M_SYSERR, TEXT("RegisterDNS: Failed to release regsiter-dns semaphore:"));
+ }
+ else
+ {
+ MsgToEventLog (M_ERR, TEXT("RegisterDNS: Failed to lock register-dns semaphore"));
+ err = ERROR_SEM_TIMEOUT; /* Windows error code 0x79 */
+ }
+ return err;
+}
+
+static DWORD
+HandleRegisterDNSMessage (void)
+{
+ DWORD err;
+ HANDLE thread = NULL;
+
+ /* Delegate this job to a sub-thread */
+ thread = CreateThread (NULL, 0, RegisterDNS, NULL, 0, NULL);
+
+ /*
+ * We don't add these thread handles to the undo list -- the thread and
+ * processes it spawns are all supposed to terminate or timeout by themselves.
+ */
+ if (thread)
+ {
+ err = 0;
+ CloseHandle (thread);
+ }
+ else
+ err = GetLastError();
+
+ return err;
+}
+
+static VOID
+HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
+{
+ DWORD read;
+ union {
+ message_header_t header;
+ address_message_t address;
+ route_message_t route;
+ flush_neighbors_message_t flush_neighbors;
+ block_dns_message_t block_dns;
+ } msg;
+ ack_message_t ack = {
+ .header = {
+ .type = msg_acknowledgement,
+ .size = sizeof (ack),
+ .message_id = -1
+ },
+ .error_number = ERROR_MESSAGE_DATA
+ };
+
+ read = ReadPipeAsync (pipe, &msg, bytes, count, events);
+ if (read != bytes || read < sizeof (msg.header) || read != msg.header.size)
+ goto out;
+
+ ack.header.message_id = msg.header.message_id;
+
+ switch (msg.header.type)
+ {
+ case msg_add_address:
+ case msg_del_address:
+ if (msg.header.size == sizeof (msg.address))
+ ack.error_number = HandleAddressMessage (&msg.address, lists);
+ break;
+
+ case msg_add_route:
+ case msg_del_route:
+ if (msg.header.size == sizeof (msg.route))
+ ack.error_number = HandleRouteMessage (&msg.route, lists);
+ break;
+
+ case msg_flush_neighbors:
+ if (msg.header.size == sizeof (msg.flush_neighbors))
+ ack.error_number = HandleFlushNeighborsMessage (&msg.flush_neighbors);
+ break;
+
+ case msg_add_block_dns:
+ case msg_del_block_dns:
+ if (msg.header.size == sizeof (msg.block_dns))
+ ack.error_number = HandleBlockDNSMessage (&msg.block_dns, lists);
+ break;
+
+ case msg_register_dns:
+ ack.error_number = HandleRegisterDNSMessage ();
+ break;
+
+ default:
+ ack.error_number = ERROR_MESSAGE_TYPE;
+ MsgToEventLog (MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type);
+ break;
+ }
+
+out:
+ WritePipeAsync (pipe, &ack, sizeof (ack), count, events);
+}
+
+
+static VOID
+Undo (undo_lists_t *lists)
+{
+ undo_type_t type;
+ for (type = 0; type < _undo_type_max; type++)
+ {
+ list_item_t **pnext = &(*lists)[type];
+ while (*pnext)
+ {
+ list_item_t *item = *pnext;
+ switch (type)
+ {
+ case address:
+ DeleteAddress (item->data);
+ break;
+
+ case route:
+ DeleteRoute (item->data);
+ break;
+
+ case block_dns:
+ delete_block_dns_filters (item->data);
+ item->data = NULL;
+ break;
+ }
+
+ /* Remove from the list and free memory */
+ *pnext = item->next;
+ free (item->data);
+ free (item);
+ }
+ }
+}
+
+static DWORD WINAPI
+RunOpenvpn (LPVOID p)
+{
+ HANDLE pipe = p;
+ HANDLE ovpn_pipe, svc_pipe;
+ PTOKEN_USER svc_user, ovpn_user;
+ HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
+ HANDLE stdin_read = NULL, stdin_write = NULL;
+ HANDLE stdout_write = NULL;
+ DWORD pipe_mode, len, exit_code = 0;
+ STARTUP_DATA sud = { 0, 0, 0 };
+ STARTUPINFOW startup_info;
+ PROCESS_INFORMATION proc_info;
+ LPVOID user_env = NULL;
+ TCHAR ovpn_pipe_name[36];
+ LPCWSTR exe_path;
+ WCHAR *cmdline = NULL;
+ size_t cmdline_size;
+ undo_lists_t undo_lists;
+
+ SECURITY_ATTRIBUTES inheritable = {
+ .nLength = sizeof (inheritable),
+ .lpSecurityDescriptor = NULL,
+ .bInheritHandle = TRUE
+ };
+
+ PACL ovpn_dacl;
+ EXPLICIT_ACCESS ea[2];
+ SECURITY_DESCRIPTOR ovpn_sd;
+ SECURITY_ATTRIBUTES ovpn_sa = {
+ .nLength = sizeof (ovpn_sa),
+ .lpSecurityDescriptor = &ovpn_sd,
+ .bInheritHandle = FALSE
+ };
+
+ ZeroMemory (&ea, sizeof (ea));
+ ZeroMemory (&startup_info, sizeof (startup_info));
+ ZeroMemory (&undo_lists, sizeof (undo_lists));
+ ZeroMemory (&proc_info, sizeof (proc_info));
+
+ if (!GetStartupData (pipe, &sud))
+ goto out;
+
+ if (!InitializeSecurityDescriptor (&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
+ {
+ ReturnLastError (pipe, L"InitializeSecurityDescriptor");
+ goto out;
+ }
+
+ /* Get SID of user the service is running under */
+ if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &svc_token))
+ {
+ ReturnLastError (pipe, L"OpenProcessToken");
+ goto out;
+ }
+ len = 0;
+ svc_user = NULL;
+ while (!GetTokenInformation (svc_token, TokenUser, svc_user, len, &len))
+ {
+ if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+ {
+ ReturnLastError (pipe, L"GetTokenInformation (service token)");
+ goto out;
+ }
+ free (svc_user);
+ svc_user = malloc (len);
+ if (svc_user == NULL)
+ {
+ ReturnLastError (pipe, L"malloc (service token user)");
+ goto out;
+ }
+ }
+ if (!IsValidSid (svc_user->User.Sid))
+ {
+ ReturnLastError (pipe, L"IsValidSid (service token user)");
+ goto out;
+ }
+
+ if (!ImpersonateNamedPipeClient (pipe))
+ {
+ ReturnLastError (pipe, L"ImpersonateNamedPipeClient");
+ goto out;
+ }
+ if (!OpenThreadToken (GetCurrentThread (), TOKEN_ALL_ACCESS, FALSE, &imp_token))
+ {
+ ReturnLastError (pipe, L"OpenThreadToken");
+ goto out;
+ }
+ len = 0;
+ ovpn_user = NULL;
+ while (!GetTokenInformation (imp_token, TokenUser, ovpn_user, len, &len))
+ {
+ if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+ {
+ ReturnLastError (pipe, L"GetTokenInformation (impersonation token)");
+ goto out;
+ }
+ free (ovpn_user);
+ ovpn_user = malloc (len);
+ if (ovpn_user == NULL)
+ {
+ ReturnLastError (pipe, L"malloc (impersonation token user)");
+ goto out;
+ }
+ }
+ if (!IsValidSid (ovpn_user->User.Sid))
+ {
+ ReturnLastError (pipe, L"IsValidSid (impersonation token user)");
+ goto out;
+ }
+
+ /* Check user is authorized or options are white-listed */
+ if (!IsAuthorizedUser (ovpn_user->User.Sid, &settings) &&
+ !ValidateOptions (pipe, sud.directory, sud.options))
+ {
+ goto out;
+ }
+
+ /* OpenVPN process DACL entry for access by service and user */
+ ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
+ ea[0].grfAccessMode = SET_ACCESS;
+ ea[0].grfInheritance = NO_INHERITANCE;
+ ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
+ ea[0].Trustee.ptstrName = (LPTSTR) svc_user->User.Sid;
+ ea[1].grfAccessPermissions = READ_CONTROL | SYNCHRONIZE | PROCESS_VM_READ |
+ SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
+ ea[1].grfAccessMode = SET_ACCESS;
+ ea[1].grfInheritance = NO_INHERITANCE;
+ ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
+ ea[1].Trustee.ptstrName = (LPTSTR) ovpn_user->User.Sid;
+
+ /* Set owner and DACL of OpenVPN security descriptor */
+ if (!SetSecurityDescriptorOwner (&ovpn_sd, svc_user->User.Sid, FALSE))
+ {
+ ReturnLastError (pipe, L"SetSecurityDescriptorOwner");
+ goto out;
+ }
+ if (SetEntriesInAcl (2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
+ {
+ ReturnLastError (pipe, L"SetEntriesInAcl");
+ goto out;
+ }
+ if (!SetSecurityDescriptorDacl (&ovpn_sd, TRUE, ovpn_dacl, FALSE))
+ {
+ ReturnLastError (pipe, L"SetSecurityDescriptorDacl");
+ goto out;
+ }
+
+ /* Create primary token from impersonation token */
+ if (!DuplicateTokenEx (imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
+ {
+ ReturnLastError (pipe, L"DuplicateTokenEx");
+ goto out;
+ }
+
+ /* use /dev/null for stdout of openvpn (client should use --log for output) */
+ stdout_write = CreateFile(_T("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE,
+ &inheritable, OPEN_EXISTING, 0, NULL);
+ if (stdout_write == INVALID_HANDLE_VALUE)
+ {
+ ReturnLastError (pipe, L"CreateFile for stdout");
+ goto out;
+ }
+
+ if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0) ||
+ !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
+ {
+ ReturnLastError (pipe, L"CreatePipe");
+ goto out;
+ }
+
+ openvpn_sntprintf (ovpn_pipe_name, _countof (ovpn_pipe_name),
+ TEXT("\\\\.\\pipe\\openvpn\\service_%lu"), GetCurrentThreadId ());
+ ovpn_pipe = CreateNamedPipe (ovpn_pipe_name,
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 128, 128, 0, NULL);
+ if (ovpn_pipe == INVALID_HANDLE_VALUE)
+ {
+ ReturnLastError (pipe, L"CreateNamedPipe");
+ goto out;
+ }
+
+ svc_pipe = CreateFile (ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0,
+ &inheritable, OPEN_EXISTING, 0, NULL);
+ if (svc_pipe == INVALID_HANDLE_VALUE)
+ {
+ ReturnLastError (pipe, L"CreateFile");
+ goto out;
+ }
+
+ pipe_mode = PIPE_READMODE_MESSAGE;
+ if (!SetNamedPipeHandleState (svc_pipe, &pipe_mode, NULL, NULL))
+ {
+ ReturnLastError (pipe, L"SetNamedPipeHandleState");
+ goto out;
+ }
+
+ cmdline_size = wcslen (sud.options) + 128;
+ cmdline = malloc (cmdline_size * sizeof (*cmdline));
+ if (cmdline == NULL)
+ {
+ ReturnLastError (pipe, L"malloc");
+ goto out;
+ }
+ openvpn_sntprintf (cmdline, cmdline_size, L"openvpn %s --msg-channel %lu",
+ sud.options, svc_pipe);
+
+ if (!CreateEnvironmentBlock (&user_env, imp_token, FALSE))
+ {
+ ReturnLastError (pipe, L"CreateEnvironmentBlock");
+ goto out;
+ }
+
+ startup_info.cb = sizeof (startup_info);
+ startup_info.lpDesktop = L"winsta0\\default";
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+ startup_info.hStdInput = stdin_read;
+ startup_info.hStdOutput = stdout_write;
+ startup_info.hStdError = stdout_write;
+
+#ifdef UNICODE
+ exe_path = settings.exe_path;
+#else
+ WCHAR wide_path[MAX_PATH];
+ MultiByteToWideChar (CP_UTF8, 0, settings.exe_path, MAX_PATH, wide_path, MAX_PATH);
+ exe_path = wide_path;
+#endif
+
+ // TODO: make sure HKCU is correct or call LoadUserProfile()
+ if (!CreateProcessAsUserW (pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
+ settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
+ user_env, sud.directory, &startup_info, &proc_info))
+ {
+ ReturnLastError (pipe, L"CreateProcessAsUser");
+ goto out;
+ }
+
+ if (!RevertToSelf ())
+ {
+ TerminateProcess (proc_info.hProcess, 1);
+ ReturnLastError (pipe, L"RevertToSelf");
+ goto out;
+ }
+
+ ReturnProcessId (pipe, proc_info.dwProcessId, 1, &exit_event);
+
+ CloseHandleEx (&stdout_write);
+ CloseHandleEx (&stdin_read);
+ CloseHandleEx (&svc_pipe);
+
+ DWORD input_size = WideCharToMultiByte (CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL);
+ LPSTR input = NULL;
+ if (input_size && (input = malloc (input_size)))
+ {
+ DWORD written;
+ WideCharToMultiByte (CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
+ WriteFile (stdin_write, input, strlen (input), &written, NULL);
+ free (input);
+ }
+
+ while (TRUE)
+ {
+ DWORD bytes = PeekNamedPipeAsync (ovpn_pipe, 1, &exit_event);
+ if (bytes == 0)
+ break;
+
+ HandleMessage (ovpn_pipe, bytes, 1, &exit_event, &undo_lists);
+ }
+
+ WaitForSingleObject (proc_info.hProcess, IO_TIMEOUT);
+ GetExitCodeProcess (proc_info.hProcess, &exit_code);
+ if (exit_code == STILL_ACTIVE)
+ TerminateProcess (proc_info.hProcess, 1);
+ else if (exit_code != 0)
+ {
+ WCHAR buf[256];
+ int len = _snwprintf (buf, _countof (buf),
+ L"OpenVPN exited with error: exit code = %lu", exit_code);
+ buf[_countof (buf) - 1] = L'\0';
+ ReturnError (pipe, ERROR_OPENVPN_STARTUP, buf, 1, &exit_event);
+ }
+ Undo (&undo_lists);
+
+out:
+ FlushFileBuffers (pipe);
+ DisconnectNamedPipe (pipe);
+
+ free (ovpn_user);
+ free (svc_user);
+ free (cmdline);
+ DestroyEnvironmentBlock (user_env);
+ FreeStartupData (&sud);
+ CloseHandleEx (&proc_info.hProcess);
+ CloseHandleEx (&proc_info.hThread);
+ CloseHandleEx (&stdin_read);
+ CloseHandleEx (&stdin_write);
+ CloseHandleEx (&stdout_write);
+ CloseHandleEx (&svc_token);
+ CloseHandleEx (&imp_token);
+ CloseHandleEx (&pri_token);
+ CloseHandleEx (&ovpn_pipe);
+ CloseHandleEx (&svc_pipe);
+ CloseHandleEx (&pipe);
+
+ return 0;
+}
+
+
+static DWORD WINAPI
+ServiceCtrlInteractive (DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
+{
+ SERVICE_STATUS *status = ctx;
+ switch (ctrl_code)
+ {
+ case SERVICE_CONTROL_STOP:
+ status->dwCurrentState = SERVICE_STOP_PENDING;
+ ReportStatusToSCMgr (service, status);
+ if (exit_event)
+ SetEvent (exit_event);
+ return NO_ERROR;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ return NO_ERROR;
+
+ default:
+ return ERROR_CALL_NOT_IMPLEMENTED;
+ }
+}
+
+
+static HANDLE
+CreateClientPipeInstance (VOID)
+{
+ HANDLE pipe = NULL;
+ PACL old_dacl, new_dacl;
+ PSECURITY_DESCRIPTOR sd;
+ static EXPLICIT_ACCESS ea[2];
+ static BOOL initialized = FALSE;
+ DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
+
+ if (!initialized)
+ {
+ PSID everyone, anonymous;
+
+ ConvertStringSidToSid (TEXT("S-1-1-0"), &everyone);
+ ConvertStringSidToSid (TEXT("S-1-5-7"), &anonymous);
+
+ ea[0].grfAccessPermissions = FILE_GENERIC_WRITE;
+ ea[0].grfAccessMode = GRANT_ACCESS;
+ ea[0].grfInheritance = NO_INHERITANCE;
+ ea[0].Trustee.pMultipleTrustee = NULL;
+ ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
+ ea[0].Trustee.ptstrName = (LPTSTR) everyone;
+
+ ea[1].grfAccessPermissions = 0;
+ ea[1].grfAccessMode = REVOKE_ACCESS;
+ ea[1].grfInheritance = NO_INHERITANCE;
+ ea[1].Trustee.pMultipleTrustee = NULL;
+ ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
+ ea[1].Trustee.ptstrName = (LPTSTR) anonymous;
+
+ flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
+ initialized = TRUE;
+ }
+
+ pipe = CreateNamedPipe (TEXT("\\\\.\\pipe\\openvpn\\service"), flags,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
+ PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, NULL);
+ if (pipe == INVALID_HANDLE_VALUE)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("Could not create named pipe"));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (GetSecurityInfo (pipe, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
+ NULL, NULL, &old_dacl, NULL, &sd) != ERROR_SUCCESS)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("Could not get pipe security info"));
+ return CloseHandleEx (&pipe);
+ }
+
+ if (SetEntriesInAcl (2, ea, old_dacl, &new_dacl) != ERROR_SUCCESS)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("Could not set entries in new acl"));
+ return CloseHandleEx (&pipe);
+ }
+
+ if (SetSecurityInfo (pipe, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
+ NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("Could not set pipe security info"));
+ return CloseHandleEx (&pipe);
+ }
+
+ return pipe;
+}
+
+
+static DWORD
+UpdateWaitHandles (LPHANDLE *handles_ptr, LPDWORD count,
+ HANDLE io_event, HANDLE exit_event, list_item_t *threads)
+{
+ static DWORD size = 10;
+ static LPHANDLE handles = NULL;
+ DWORD pos = 0;
+
+ if (handles == NULL)
+ {
+ handles = malloc (size * sizeof (HANDLE));
+ *handles_ptr = handles;
+ if (handles == NULL)
+ return ERROR_OUTOFMEMORY;
+ }
+
+ handles[pos++] = io_event;
+
+ if (!threads)
+ handles[pos++] = exit_event;
+
+ while (threads)
+ {
+ if (pos == size)
+ {
+ LPHANDLE tmp;
+ size += 10;
+ tmp = realloc (handles, size * sizeof (HANDLE));
+ if (tmp == NULL)
+ {
+ size -= 10;
+ *count = pos;
+ return ERROR_OUTOFMEMORY;
+ }
+ handles = tmp;
+ *handles_ptr = handles;
+ }
+ handles[pos++] = threads->data;
+ threads = threads->next;
+ }
+
+ *count = pos;
+ return NO_ERROR;
+}
+
+
+static VOID
+FreeWaitHandles (LPHANDLE h)
+{
+ free (h);
+}
+
+
+VOID WINAPI
+ServiceStartInteractive (DWORD dwArgc, LPTSTR *lpszArgv)
+{
+ HANDLE pipe, io_event = NULL;
+ OVERLAPPED overlapped;
+ DWORD error = NO_ERROR;
+ list_item_t *threads = NULL;
+ PHANDLE handles = NULL;
+ DWORD handle_count;
+ BOOL CmpHandle (LPVOID item, LPVOID hnd) { return item == hnd; }
+
+ service = RegisterServiceCtrlHandlerEx (interactive_service.name, ServiceCtrlInteractive, &status);
+ if (!service)
+ return;
+
+ status.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
+ status.dwCurrentState = SERVICE_START_PENDING;
+ status.dwServiceSpecificExitCode = NO_ERROR;
+ status.dwWin32ExitCode = NO_ERROR;
+ status.dwWaitHint = 3000;
+ ReportStatusToSCMgr (service, &status);
+
+ /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */
+ error = GetOpenvpnSettings (&settings);
+ if (error != ERROR_SUCCESS)
+ goto out;
+
+ io_event = InitOverlapped (&overlapped);
+ exit_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (!exit_event || !io_event)
+ {
+ error = MsgToEventLog (M_SYSERR, TEXT("Could not create event"));
+ goto out;
+ }
+
+ rdns_semaphore = CreateSemaphoreW (NULL, 1, 1, NULL);
+ if (!rdns_semaphore)
+ {
+ error = MsgToEventLog (M_SYSERR, TEXT("Could not create semaphore for register-dns"));
+ goto out;
+ }
+
+ error = UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads);
+ if (error != NO_ERROR)
+ goto out;
+
+ pipe = CreateClientPipeInstance ();
+ if (pipe == INVALID_HANDLE_VALUE)
+ goto out;
+
+ status.dwCurrentState = SERVICE_RUNNING;
+ status.dwWaitHint = 0;
+ ReportStatusToSCMgr (service, &status);
+
+ while (TRUE)
+ {
+ if (ConnectNamedPipe (pipe, &overlapped) == FALSE &&
+ GetLastError () != ERROR_PIPE_CONNECTED &&
+ GetLastError () != ERROR_IO_PENDING)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("Could not connect pipe"));
+ break;
+ }
+
+ error = WaitForMultipleObjects (handle_count, handles, FALSE, INFINITE);
+ if (error == WAIT_OBJECT_0)
+ {
+ /* Client connected, spawn a worker thread for it */
+ HANDLE next_pipe = CreateClientPipeInstance ();
+ HANDLE thread = CreateThread (NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
+ if (thread)
+ {
+ error = AddListItem (&threads, thread);
+ if (!error)
+ error = UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads);
+ if (error)
+ {
+ ReturnError (pipe, error, L"Insufficient resources to service new clients", 1, &exit_event);
+ /* Update wait handles again after removing the last worker thread */
+ RemoveListItem (&threads, CmpHandle, thread);
+ UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads);
+ TerminateThread (thread, 1);
+ CloseHandleEx (&thread);
+ CloseHandleEx (&pipe);
+ }
+ else
+ ResumeThread (thread);
+ }
+ else
+ CloseHandleEx (&pipe);
+
+ ResetOverlapped (&overlapped);
+ pipe = next_pipe;
+ }
+ else
+ {
+ CancelIo (pipe);
+ if (error == WAIT_FAILED)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("WaitForMultipleObjects failed"));
+ SetEvent (exit_event);
+ /* Give some time for worker threads to exit and then terminate */
+ Sleep (1000);
+ break;
+ }
+ if (!threads)
+ {
+ /* exit event signaled */
+ CloseHandleEx (&pipe);
+ ResetEvent (exit_event);
+ error = NO_ERROR;
+ break;
+ }
+
+ /* Worker thread ended */
+ HANDLE thread = RemoveListItem (&threads, CmpHandle, handles[error]);
+ UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads);
+ CloseHandleEx (&thread);
+ }
+ }
+
+out:
+ FreeWaitHandles (handles);
+ CloseHandleEx (&io_event);
+ CloseHandleEx (&exit_event);
+ CloseHandleEx (&rdns_semaphore);
+
+ status.dwCurrentState = SERVICE_STOPPED;
+ status.dwWin32ExitCode = error;
+ ReportStatusToSCMgr (service, &status);
+}
diff --git a/src/openvpnserv/openvpnserv.c b/src/openvpnserv/openvpnserv.c
deleted file mode 100755
index 56f5a02..0000000
--- a/src/openvpnserv/openvpnserv.c
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * OpenVPN -- An application to securely tunnel IP networks
- * over a single TCP/UDP port, with support for SSL/TLS-based
- * session authentication and key exchange,
- * packet encryption, packet authentication, and
- * packet compression.
- *
- * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * 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 (see the file COPYING included with this
- * distribution); if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/*
- * This program allows one or more OpenVPN processes to be started
- * as a service. To build, you must get the service sample from the
- * Platform SDK and replace Simple.c with this file.
- *
- * You should also apply service.patch to
- * service.c and service.h from the Platform SDK service sample.
- *
- * This code is designed to be built with the mingw compiler.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#elif defined(_MSC_VER)
-#include "config-msvc.h"
-#endif
-#include <windows.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <process.h>
-#include "service.h"
-
-/* bool definitions */
-#define bool int
-#define true 1
-#define false 0
-
-/* These are new for 2000/XP, so they aren't in the mingw headers yet */
-#ifndef BELOW_NORMAL_PRIORITY_CLASS
-#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000
-#endif
-#ifndef ABOVE_NORMAL_PRIORITY_CLASS
-#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
-#endif
-
-struct security_attributes
-{
- SECURITY_ATTRIBUTES sa;
- SECURITY_DESCRIPTOR sd;
-};
-
-/*
- * This event is initially created in the non-signaled
- * state. It will transition to the signaled state when
- * we have received a terminate signal from the Service
- * Control Manager which will cause an asynchronous call
- * of ServiceStop below.
- */
-#define EXIT_EVENT_NAME PACKAGE "_exit_1"
-
-/*
- * Which registry key in HKLM should
- * we get config info from?
- */
-#define REG_KEY "SOFTWARE\\" PACKAGE_NAME
-
-static HANDLE exit_event = NULL;
-
-/* clear an object */
-#define CLEAR(x) memset(&(x), 0, sizeof(x))
-
-/*
- * Message handling
- */
-#define M_INFO (0) /* informational */
-#define M_SYSERR (MSG_FLAGS_ERROR|MSG_FLAGS_SYS_CODE) /* error + system code */
-#define M_ERR (MSG_FLAGS_ERROR) /* error */
-
-/* write error to event log */
-#define MSG(flags, ...) \
- { \
- char x_msg[256]; \
- openvpn_snprintf (x_msg, sizeof(x_msg), __VA_ARGS__); \
- AddToMessageLog ((flags), x_msg); \
- }
-
-/* get a registry string */
-#define QUERY_REG_STRING(name, data) \
- { \
- len = sizeof (data); \
- status = RegQueryValueEx(openvpn_key, name, NULL, &type, data, &len); \
- if (status != ERROR_SUCCESS || type != REG_SZ) \
- { \
- SetLastError (status); \
- MSG (M_SYSERR, error_format_str, name); \
- RegCloseKey (openvpn_key); \
- goto finish; \
- } \
- }
-
-/* get a registry string */
-#define QUERY_REG_DWORD(name, data) \
- { \
- len = sizeof (DWORD); \
- status = RegQueryValueEx(openvpn_key, name, NULL, &type, (LPBYTE)&data, &len); \
- if (status != ERROR_SUCCESS || type != REG_DWORD || len != sizeof (DWORD)) \
- { \
- SetLastError (status); \
- MSG (M_SYSERR, error_format_dword, name); \
- RegCloseKey (openvpn_key); \
- goto finish; \
- } \
- }
-
-/*
- * This is necessary due to certain buggy implementations of snprintf,
- * that don't guarantee null termination for size > 0.
- * (copied from ../buffer.c, line 217)
- * (git: 100644 blob e2f8caab0a5b2a870092c6cd508a1a50c21c3ba3 buffer.c)
- */
-
-int openvpn_snprintf(char *str, size_t size, const char *format, ...)
-{
- va_list arglist;
- int len = -1;
- if (size > 0)
- {
- va_start (arglist, format);
- len = vsnprintf (str, size, format, arglist);
- va_end (arglist);
- str[size - 1] = 0;
- }
- return (len >= 0 && len < size);
-}
-
-
-bool
-init_security_attributes_allow_all (struct security_attributes *obj)
-{
- CLEAR (*obj);
-
- obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES);
- obj->sa.lpSecurityDescriptor = &obj->sd;
- obj->sa.bInheritHandle = TRUE;
- if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION))
- return false;
- if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE))
- return false;
- return true;
-}
-
-HANDLE
-create_event (const char *name, bool allow_all, bool initial_state, bool manual_reset)
-{
- if (allow_all)
- {
- struct security_attributes sa;
- if (!init_security_attributes_allow_all (&sa))
- return NULL;
- return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name);
- }
- else
- return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name);
-}
-
-void
-close_if_open (HANDLE h)
-{
- if (h != NULL)
- CloseHandle (h);
-}
-
-static bool
-match (const WIN32_FIND_DATA *find, const char *ext)
-{
- int i;
-
- if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- return false;
-
- if (!strlen (ext))
- return true;
-
- i = strlen (find->cFileName) - strlen (ext) - 1;
- if (i < 1)
- return false;
-
- return find->cFileName[i] == '.' && !_stricmp (find->cFileName + i + 1, ext);
-}
-
-/*
- * Modify the extension on a filename.
- */
-static bool
-modext (char *dest, int size, const char *src, const char *newext)
-{
- int i;
-
- if (size > 0 && (strlen (src) + 1) <= size)
- {
- strcpy (dest, src);
- dest [size - 1] = '\0';
- i = strlen (dest);
- while (--i >= 0)
- {
- if (dest[i] == '\\')
- break;
- if (dest[i] == '.')
- {
- dest[i] = '\0';
- break;
- }
- }
- if (strlen (dest) + strlen(newext) + 2 <= size)
- {
- strcat (dest, ".");
- strcat (dest, newext);
- return true;
- }
- dest [0] = '\0';
- }
- return false;
-}
-
-VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
-{
- char exe_path[MAX_PATH];
- char config_dir[MAX_PATH];
- char ext_string[16];
- char log_dir[MAX_PATH];
- char priority_string[64];
- char append_string[2];
-
- DWORD priority;
- bool append;
-
- ResetError ();
-
- if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
- {
- MSG (M_ERR, "ReportStatusToSCMgr #1 failed");
- goto finish;
- }
-
- /*
- * Create our exit event
- */
- exit_event = create_event (EXIT_EVENT_NAME, false, false, true);
- if (!exit_event)
- {
- MSG (M_ERR, "CreateEvent failed");
- goto finish;
- }
-
- /*
- * If exit event is already signaled, it means we were not
- * shut down properly.
- */
- if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT)
- {
- MSG (M_ERR, "Exit event is already signaled -- we were not shut down properly");
- goto finish;
- }
-
- if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
- {
- MSG (M_ERR, "ReportStatusToSCMgr #2 failed");
- goto finish;
- }
-
- /*
- * Read info from registry in key HKLM\SOFTWARE\OpenVPN
- */
- {
- HKEY openvpn_key;
- LONG status;
- DWORD len;
- DWORD type;
-
- static const char error_format_str[] =
- "Error querying registry key of type REG_SZ: HKLM\\" REG_KEY "\\%s";
-
- static const char error_format_dword[] =
- "Error querying registry key of type REG_DWORD: HKLM\\" REG_KEY "\\%s";
-
- status = RegOpenKeyEx(
- HKEY_LOCAL_MACHINE,
- REG_KEY,
- 0,
- KEY_READ,
- &openvpn_key);
-
- if (status != ERROR_SUCCESS)
- {
- SetLastError (status);
- MSG (M_SYSERR, "Registry key HKLM\\" REG_KEY " not found");
- goto finish;
- }
-
- /* get path to openvpn.exe */
- QUERY_REG_STRING ("exe_path", exe_path);
-
- /* get path to configuration directory */
- QUERY_REG_STRING ("config_dir", config_dir);
-
- /* get extension on configuration files */
- QUERY_REG_STRING ("config_ext", ext_string);
-
- /* get path to log directory */
- QUERY_REG_STRING ("log_dir", log_dir);
-
- /* get priority for spawned OpenVPN subprocesses */
- QUERY_REG_STRING ("priority", priority_string);
-
- /* should we truncate or append to logfile? */
- QUERY_REG_STRING ("log_append", append_string);
-
- RegCloseKey (openvpn_key);
- }
-
- /* set process priority */
- priority = NORMAL_PRIORITY_CLASS;
- if (!_stricmp (priority_string, "IDLE_PRIORITY_CLASS"))
- priority = IDLE_PRIORITY_CLASS;
- else if (!_stricmp (priority_string, "BELOW_NORMAL_PRIORITY_CLASS"))
- priority = BELOW_NORMAL_PRIORITY_CLASS;
- else if (!_stricmp (priority_string, "NORMAL_PRIORITY_CLASS"))
- priority = NORMAL_PRIORITY_CLASS;
- else if (!_stricmp (priority_string, "ABOVE_NORMAL_PRIORITY_CLASS"))
- priority = ABOVE_NORMAL_PRIORITY_CLASS;
- else if (!_stricmp (priority_string, "HIGH_PRIORITY_CLASS"))
- priority = HIGH_PRIORITY_CLASS;
- else
- {
- MSG (M_ERR, "Unknown priority name: %s", priority_string);
- goto finish;
- }
-
- /* set log file append/truncate flag */
- append = false;
- if (append_string[0] == '0')
- append = false;
- else if (append_string[0] == '1')
- append = true;
- else
- {
- MSG (M_ERR, "Log file append flag (given as '%s') must be '0' or '1'", append_string);
- goto finish;
- }
-
- /*
- * Instantiate an OpenVPN process for each configuration
- * file found.
- */
- {
- WIN32_FIND_DATA find_obj;
- HANDLE find_handle;
- BOOL more_files;
- char find_string[MAX_PATH];
-
- openvpn_snprintf (find_string, MAX_PATH, "%s\\*", config_dir);
-
- find_handle = FindFirstFile (find_string, &find_obj);
- if (find_handle == INVALID_HANDLE_VALUE)
- {
- MSG (M_ERR, "Cannot get configuration file list using: %s", find_string);
- goto finish;
- }
-
- /*
- * Loop over each config file
- */
- do {
- HANDLE log_handle = NULL;
- STARTUPINFO start_info;
- PROCESS_INFORMATION proc_info;
- struct security_attributes sa;
- char log_file[MAX_PATH];
- char log_path[MAX_PATH];
- char command_line[256];
-
- CLEAR (start_info);
- CLEAR (proc_info);
- CLEAR (sa);
-
- if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
- {
- MSG (M_ERR, "ReportStatusToSCMgr #3 failed");
- FindClose (find_handle);
- goto finish;
- }
-
- /* does file have the correct type and extension? */
- if (match (&find_obj, ext_string))
- {
- /* get log file pathname */
- if (!modext (log_file, sizeof (log_file), find_obj.cFileName, "log"))
- {
- MSG (M_ERR, "Cannot construct logfile name based on: %s", find_obj.cFileName);
- FindClose (find_handle);
- goto finish;
- }
- openvpn_snprintf (log_path, sizeof(log_path),
- "%s\\%s", log_dir, log_file);
-
- /* construct command line */
- openvpn_snprintf (command_line, sizeof(command_line), PACKAGE " --service %s 1 --config \"%s\"",
- EXIT_EVENT_NAME,
- find_obj.cFileName);
-
- /* Make security attributes struct for logfile handle so it can
- be inherited. */
- if (!init_security_attributes_allow_all (&sa))
- {
- MSG (M_SYSERR, "InitializeSecurityDescriptor start_" PACKAGE " failed");
- goto finish;
- }
-
- /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */
- log_handle = CreateFile (log_path,
- GENERIC_WRITE,
- FILE_SHARE_READ,
- &sa.sa,
- append ? OPEN_ALWAYS : CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (log_handle == INVALID_HANDLE_VALUE)
- {
- MSG (M_SYSERR, "Cannot open logfile: %s", log_path);
- FindClose (find_handle);
- goto finish;
- }
-
- /* append to logfile? */
- if (append)
- {
- if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
- {
- MSG (M_SYSERR, "Cannot seek to end of logfile: %s", log_path);
- FindClose (find_handle);
- goto finish;
- }
- }
-
- /* fill in STARTUPINFO struct */
- GetStartupInfo(&start_info);
- start_info.cb = sizeof(start_info);
- start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
- start_info.wShowWindow = SW_HIDE;
- start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
- start_info.hStdOutput = start_info.hStdError = log_handle;
-
- /* create an OpenVPN process for one config file */
- if (!CreateProcess(exe_path,
- command_line,
- NULL,
- NULL,
- TRUE,
- priority | CREATE_NEW_CONSOLE,
- NULL,
- config_dir,
- &start_info,
- &proc_info))
- {
- MSG (M_SYSERR, "CreateProcess failed, exe='%s' cmdline='%s' dir='%s'",
- exe_path,
- command_line,
- config_dir);
-
- FindClose (find_handle);
- CloseHandle (log_handle);
- goto finish;
- }
-
- /* close unneeded handles */
- Sleep (1000); /* try to prevent race if we close logfile
- handle before child process DUPs it */
- if (!CloseHandle (proc_info.hProcess)
- || !CloseHandle (proc_info.hThread)
- || !CloseHandle (log_handle))
- {
- MSG (M_SYSERR, "CloseHandle failed");
- goto finish;
- }
- }
-
- /* more files to process? */
- more_files = FindNextFile (find_handle, &find_obj);
-
- } while (more_files);
-
- FindClose (find_handle);
- }
-
- /* we are now fully started */
- if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
- {
- MSG (M_ERR, "ReportStatusToSCMgr SERVICE_RUNNING failed");
- goto finish;
- }
-
- /* wait for our shutdown signal */
- if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0)
- {
- MSG (M_ERR, "wait for shutdown signal failed");
- }
-
- finish:
- ServiceStop ();
- if (exit_event)
- CloseHandle (exit_event);
-}
-
-VOID ServiceStop()
-{
- if (exit_event)
- SetEvent(exit_event);
-}
diff --git a/src/openvpnserv/openvpnserv.vcxproj b/src/openvpnserv/openvpnserv.vcxproj
index 2a8943d..c6760da 100644
--- a/src/openvpnserv/openvpnserv.vcxproj
+++ b/src/openvpnserv/openvpnserv.vcxproj
@@ -111,4 +111,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/openvpnserv/service.c b/src/openvpnserv/service.c
index d7562b3..82f5551 100644
--- a/src/openvpnserv/service.c
+++ b/src/openvpnserv/service.c
@@ -1,700 +1,245 @@
-/*---------------------------------------------------------------------------
-THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
-ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
-PARTICULAR PURPOSE.
-
-Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
-
-MODULE: service.c
-
-PURPOSE: Implements functions required by all Windows NT services
-
-FUNCTIONS:
- main(int argc, char **argv);
- service_ctrl(DWORD dwCtrlCode);
- service_main(DWORD dwArgc, LPTSTR *lpszArgv);
- CmdInstallService();
- CmdRemoveService();
- CmdStartService();
- CmdDebugService(int argc, char **argv);
- ControlHandler ( DWORD dwCtrlType );
- GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
-
----------------------------------------------------------------------------*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#elif defined(_MSC_VER)
-#include "config-msvc.h"
-#endif
-#include <windows.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <process.h>
-#include <tchar.h>
+/*
+ * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
+ * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+ * PARTICULAR PURPOSE.
+ *
+ * Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
+ * 2013 Heiko Hund <heiko.hund@sophos.com>
+ */
#include "service.h"
-// internal variables
-SERVICE_STATUS ssStatus; // current status of the service
-SERVICE_STATUS_HANDLE sshStatusHandle;
-DWORD dwErr = 0;
-BOOL bDebug = FALSE;
-TCHAR szErr[256];
-
-// internal function prototypes
-VOID WINAPI service_ctrl(DWORD dwCtrlCode);
-VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
-int CmdInstallService();
-int CmdRemoveService();
-int CmdStartService();
-VOID CmdDebugService(int argc, char **argv);
-BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
-LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
-
-//
-// FUNCTION: main
-//
-// PURPOSE: entrypoint for service
-//
-// PARAMETERS:
-// argc - number of command line arguments
-// argv - array of command line arguments
-//
-// RETURN VALUE:
-// none
-//
-// COMMENTS:
-// main() either performs the command line task, or
-// call StartServiceCtrlDispatcher to register the
-// main service thread. When the this call returns,
-// the service has stopped, so exit.
-//
-int __cdecl main(int argc, char **argv)
-{
- SERVICE_TABLE_ENTRY dispatchTable[] =
- {
- { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
- { NULL, NULL}
- };
-
- if ( (argc > 1) &&
- ((*argv[1] == '-') || (*argv[1] == '/')) )
- {
- if ( _stricmp( "install", argv[1]+1 ) == 0 )
- {
- return CmdInstallService();
- }
- else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
- {
- return CmdRemoveService();
- }
- else if ( _stricmp( "start", argv[1]+1 ) == 0)
- {
- return CmdStartService();
- }
- else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
- {
- bDebug = TRUE;
- CmdDebugService(argc, argv);
- }
- else
- {
- goto dispatch;
- }
- return 0;
- }
-
- // if it doesn't match any of the above parameters
- // the service control manager may be starting the service
- // so we must call StartServiceCtrlDispatcher
- dispatch:
- // this is just to be friendly
- printf( "%s -install to install the service\n", SZAPPNAME );
- printf( "%s -start to start the service\n", SZAPPNAME );
- printf( "%s -remove to remove the service\n", SZAPPNAME );
- printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
- printf( "\nStartServiceCtrlDispatcher being called.\n" );
- printf( "This may take several seconds. Please wait.\n" );
-
- if (!StartServiceCtrlDispatcher(dispatchTable))
- AddToMessageLog(MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed."));
-
- return 0;
-}
-
-
-
-//
-// FUNCTION: service_main
-//
-// PURPOSE: To perform actual initialization of the service
-//
-// PARAMETERS:
-// dwArgc - number of command line arguments
-// lpszArgv - array of command line arguments
-//
-// RETURN VALUE:
-// none
-//
-// COMMENTS:
-// This routine performs the service initialization and then calls
-// the user defined ServiceStart() routine to perform majority
-// of the work.
-//
-void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
-{
-
- // register our service control handler:
- //
- sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
-
- if (!sshStatusHandle)
- goto cleanup;
-
- // SERVICE_STATUS members that don't change in example
- //
- ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- ssStatus.dwServiceSpecificExitCode = 0;
-
-
- // report the status to the service control manager.
- //
- if (!ReportStatusToSCMgr(
- SERVICE_START_PENDING, // service state
- NO_ERROR, // exit code
- 3000)) // wait hint
- goto cleanup;
-
-
- ServiceStart( dwArgc, lpszArgv );
-
- cleanup:
+#include <windows.h>
+#include <stdio.h>
+#include <process.h>
- // try to report the stopped status to the service control manager.
- //
- if (sshStatusHandle)
- (VOID)ReportStatusToSCMgr(
- SERVICE_STOPPED,
- dwErr,
- 0);
- return;
-}
+openvpn_service_t openvpn_service[_service_max];
-
-//
-// FUNCTION: service_ctrl
-//
-// PURPOSE: This function is called by the SCM whenever
-// ControlService() is called on this service.
-//
-// PARAMETERS:
-// dwCtrlCode - type of control requested
-//
-// RETURN VALUE:
-// none
-//
-// COMMENTS:
-//
-VOID WINAPI service_ctrl(DWORD dwCtrlCode)
+BOOL
+ReportStatusToSCMgr (SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status)
{
- // Handle the requested control code.
- //
- switch (dwCtrlCode)
- {
- // Stop the service.
- //
- // SERVICE_STOP_PENDING should be reported before
- // setting the Stop Event - hServerStopEvent - in
- // ServiceStop(). This avoids a race condition
- // which may result in a 1053 - The Service did not respond...
- // error.
- case SERVICE_CONTROL_STOP:
- ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
- ServiceStop();
- return;
-
- // Update the service status.
- //
- case SERVICE_CONTROL_INTERROGATE:
- break;
-
- // invalid control code
- //
- default:
- break;
-
- }
-
- ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
+ static DWORD dwCheckPoint = 1;
+ BOOL res = TRUE;
+
+ if (status->dwCurrentState == SERVICE_START_PENDING)
+ status->dwControlsAccepted = 0;
+ else
+ status->dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+ if (status->dwCurrentState == SERVICE_RUNNING ||
+ status->dwCurrentState == SERVICE_STOPPED)
+ status->dwCheckPoint = 0;
+ else
+ status->dwCheckPoint = dwCheckPoint++;
+
+ /* Report the status of the service to the service control manager. */
+ res = SetServiceStatus (service, status);
+ if (!res)
+ MsgToEventLog(MSG_FLAGS_ERROR, TEXT("SetServiceStatus"));
+
+ return res;
}
-
-
-//
-// FUNCTION: ReportStatusToSCMgr()
-//
-// PURPOSE: Sets the current status of the service and
-// reports it to the Service Control Manager
-//
-// PARAMETERS:
-// dwCurrentState - the state of the service
-// dwWin32ExitCode - error code to report
-// dwWaitHint - worst case estimate to next checkpoint
-//
-// RETURN VALUE:
-// TRUE - success
-// FALSE - failure
-//
-// COMMENTS:
-//
-BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
- DWORD dwWin32ExitCode,
- DWORD dwWaitHint)
+static int
+CmdInstallServices ()
{
- static DWORD dwCheckPoint = 1;
- BOOL fResult = TRUE;
-
-
- if ( !bDebug ) // when debugging we don't report to the SCM
- {
- if (dwCurrentState == SERVICE_START_PENDING)
- ssStatus.dwControlsAccepted = 0;
- else
- ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
-
- ssStatus.dwCurrentState = dwCurrentState;
- ssStatus.dwWin32ExitCode = dwWin32ExitCode;
- ssStatus.dwWaitHint = dwWaitHint;
+ SC_HANDLE service;
+ SC_HANDLE svc_ctl_mgr;
+ TCHAR path[512];
+ int i, ret = _service_max;
+
+ if (GetModuleFileName (NULL, path + 1, 510) == 0)
+ {
+ _tprintf (TEXT("Unable to install service - %s\n"), GetLastErrorText ());
+ return 1;
+ }
+
+ path[0] = TEXT('\"');
+ _tcscat (path, TEXT("\""));
+
+ svc_ctl_mgr = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
+ if (svc_ctl_mgr == NULL)
+ {
+ _tprintf (TEXT("OpenSCManager failed - %s\n"), GetLastErrorText ());
+ return 1;
+ }
- if ( ( dwCurrentState == SERVICE_RUNNING ) ||
- ( dwCurrentState == SERVICE_STOPPED ) )
- ssStatus.dwCheckPoint = 0;
+ for (i = 0; i < _service_max; i++)
+ {
+ service = CreateService (svc_ctl_mgr,
+ openvpn_service[i].name,
+ openvpn_service[i].display_name,
+ SERVICE_QUERY_STATUS,
+ SERVICE_WIN32_SHARE_PROCESS,
+ openvpn_service[i].start_type,
+ SERVICE_ERROR_NORMAL,
+ path, NULL, NULL,
+ openvpn_service[i].dependencies,
+ NULL, NULL);
+ if (service)
+ {
+ _tprintf (TEXT("%s installed.\n"), openvpn_service[i].display_name);
+ CloseServiceHandle (service);
+ --ret;
+ }
else
- ssStatus.dwCheckPoint = dwCheckPoint++;
-
+ _tprintf (TEXT("CreateService failed - %s\n"), GetLastErrorText ());
+ }
- // Report the status of the service to the service control manager.
- //
- if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus)))
- {
- AddToMessageLog(MSG_FLAGS_ERROR, TEXT("SetServiceStatus"));
- }
- }
- return fResult;
+ CloseServiceHandle (svc_ctl_mgr);
+ return ret;
}
-
-//
-// FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
-//
-// PURPOSE: Allows any thread to log an error message
-//
-// PARAMETERS:
-// lpszMsg - text for message
-//
-// RETURN VALUE:
-// none
-//
-// COMMENTS:
-//
-void AddToMessageLog(DWORD flags, LPTSTR lpszMsg)
+static int
+CmdStartService (openvpn_service_type type)
{
- TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ];
- HANDLE hEventSource;
- LPCSTR lpszStrings[2];
-
- if ( !bDebug )
- {
- if (flags & MSG_FLAGS_SYS_CODE)
- dwErr = GetLastError();
- else
- dwErr = 0;
-
- // Use event logging to log the error.
- //
- hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
-
- _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), (int)dwErr);
- lpszStrings[0] = szMsg;
- lpszStrings[1] = lpszMsg;
-
- if (hEventSource != NULL)
- {
- ReportEvent(hEventSource, // handle of event source
- // event type
- (flags & MSG_FLAGS_ERROR)
- ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
- 0, // event category
- 0, // event ID
- NULL, // current user's SID
- 2, // strings in lpszStrings
- 0, // no bytes of raw data
- lpszStrings, // array of error strings
- NULL); // no raw data
-
- (VOID) DeregisterEventSource(hEventSource);
- }
- }
-}
-
-void ResetError (void)
-{
- dwErr = 0;
-}
+ int ret = 1;
+ SC_HANDLE svc_ctl_mgr;
+ SC_HANDLE service;
-///////////////////////////////////////////////////////////////////
-//
-// The following code handles service installation and removal
-//
-
-
-//
-// FUNCTION: CmdInstallService()
-//
-// PURPOSE: Installs the service
-//
-// PARAMETERS:
-// none
-//
-// RETURN VALUE:
-// 0 if success
-//
-// COMMENTS:
-//
-int CmdInstallService()
-{
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
-
- TCHAR szPath[512];
-
- int ret = 0;
-
- if ( GetModuleFileName( NULL, szPath+1, 510 ) == 0 )
- {
- _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
+ svc_ctl_mgr = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (svc_ctl_mgr == NULL)
+ {
+ _tprintf (TEXT("OpenSCManager failed - %s\n"), GetLastErrorText ());
return 1;
- }
- szPath[0] = '\"';
- strcat(szPath, "\"");
-
- schSCManager = OpenSCManager(
- NULL, // machine (NULL == local)
- NULL, // database (NULL == default)
- SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required
- );
- if ( schSCManager )
- {
- schService = CreateService(
- schSCManager, // SCManager database
- TEXT(SZSERVICENAME), // name of service
- TEXT(SZSERVICEDISPLAYNAME), // name to display
- SERVICE_QUERY_STATUS, // desired access
- SERVICE_WIN32_OWN_PROCESS, // service type
- SERVICE_DEMAND_START, // start type -- alternative: SERVICE_AUTO_START
- SERVICE_ERROR_NORMAL, // error control type
- szPath, // service's binary
- NULL, // no load ordering group
- NULL, // no tag identifier
- TEXT(SZDEPENDENCIES), // dependencies
- NULL, // LocalSystem account
- NULL); // no password
-
- if ( schService )
- {
- _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
- CloseServiceHandle(schService);
- }
- else
- {
- _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
- ret = 1;
- }
-
- CloseServiceHandle(schSCManager);
- }
- else
- {
- _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
- ret = 1;
- }
- return ret;
-}
-
-//
-// FUNCTION: CmdStartService()
-//
-// PURPOSE: Start the service
-//
-// PARAMETERS:
-// none
-//
-// RETURN VALUE:
-// 0 if success
-//
-// COMMENTS:
-
-int CmdStartService()
-{
- int ret = 0;
-
- SC_HANDLE schSCManager;
- SC_HANDLE schService;
-
-
- // Open a handle to the SC Manager database.
- schSCManager = OpenSCManager(
- NULL, // local machine
- NULL, // ServicesActive database
- SC_MANAGER_ALL_ACCESS); // full access rights
-
- if (NULL == schSCManager) {
- _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
- ret = 1;
}
- schService = OpenService(
- schSCManager, // SCM database
- SZSERVICENAME, // service name
- SERVICE_ALL_ACCESS);
+ service = OpenService (svc_ctl_mgr, openvpn_service[type].name, SERVICE_ALL_ACCESS);
+ if (service)
+ {
+ if (StartService (service, 0, NULL))
+ {
+ _tprintf (TEXT("Service Started\n"));
+ ret = 0;
+ }
+ else
+ _tprintf (TEXT("StartService failed - %s\n"), GetLastErrorText ());
- if (schService == NULL) {
- _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
- ret = 1;
+ CloseServiceHandle(service);
}
-
- if (!StartService(
- schService, // handle to service
- 0, // number of arguments
- NULL) ) // no arguments
+ else
{
- _tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText(szErr,256));
- ret = 1;
+ _tprintf (TEXT("OpenService failed - %s\n"), GetLastErrorText ());
}
- else
- {
- _tprintf(TEXT("Service Started\n"));
- ret = 0;
- }
- CloseServiceHandle(schService);
- CloseServiceHandle(schSCManager);
- return ret;
-}
-//
-// FUNCTION: CmdRemoveService()
-//
-// PURPOSE: Stops and removes the service
-//
-// PARAMETERS:
-// none
-//
-// RETURN VALUE:
-// 0 if success
-//
-// COMMENTS:
-//
-int CmdRemoveService()
-{
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
+ CloseServiceHandle(svc_ctl_mgr);
+ return ret;
+}
- int ret = 0;
- schSCManager = OpenSCManager(
- NULL, // machine (NULL == local)
- NULL, // database (NULL == default)
- SC_MANAGER_CONNECT // access required
- );
- if ( schSCManager )
- {
- schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
+static int
+CmdRemoveServices ()
+{
+ SC_HANDLE service;
+ SC_HANDLE svc_ctl_mgr;
+ SERVICE_STATUS status;
+ int i, ret = _service_max;
- if (schService)
- {
- // try to stop the service
- if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
- {
- _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
- Sleep( 1000 );
+ svc_ctl_mgr = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT);
+ if (svc_ctl_mgr == NULL)
+ {
+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText ());
+ return 1;
+ }
- while ( QueryServiceStatus( schService, &ssStatus ) )
+ for (i = 0; i < _service_max; i++)
+ {
+ openvpn_service_t *ovpn_svc = &openvpn_service[i];
+ service = OpenService (svc_ctl_mgr, ovpn_svc->name,
+ DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (service == NULL)
+ {
+ _tprintf (TEXT("OpenService failed - %s\n"), GetLastErrorText ());
+ goto out;
+ }
+
+ /* try to stop the service */
+ if (ControlService (service, SERVICE_CONTROL_STOP, &status))
+ {
+ _tprintf (TEXT("Stopping %s."), ovpn_svc->display_name);
+ Sleep (1000);
+
+ while (QueryServiceStatus (service, &status))
{
- if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
- {
- _tprintf(TEXT("."));
- Sleep( 1000 );
- }
- else
- break;
+ if (status.dwCurrentState == SERVICE_STOP_PENDING)
+ {
+ _tprintf (TEXT("."));
+ Sleep (1000);
+ }
+ else
+ break;
}
- if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
- _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
- else
- {
- _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
- ret = 1;
- }
-
- }
-
- // now remove the service
- if ( DeleteService(schService) )
- _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
- else
- {
- _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
- ret = 1;
- }
-
-
- CloseServiceHandle(schService);
- }
+ if (status.dwCurrentState == SERVICE_STOPPED)
+ _tprintf (TEXT("\n%s stopped.\n"), ovpn_svc->display_name);
+ else
+ _tprintf (TEXT("\n%s failed to stop.\n"), ovpn_svc->display_name);
+ }
+
+ /* now remove the service */
+ if (DeleteService (service))
+ {
+ _tprintf (TEXT("%s removed.\n"), ovpn_svc->display_name);
+ --ret;
+ }
else
- {
- _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
- ret = 1;
- }
-
- CloseServiceHandle(schSCManager);
- }
- else
- {
- _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
- ret = 1;
- }
- return ret;
-}
-
-
-
-
-///////////////////////////////////////////////////////////////////
-//
-// The following code is for running the service as a console app
-//
-
-
-//
-// FUNCTION: CmdDebugService(int argc, char ** argv)
-//
-// PURPOSE: Runs the service as a console application
-//
-// PARAMETERS:
-// argc - number of command line arguments
-// argv - array of command line arguments
-//
-// RETURN VALUE:
-// none
-//
-// COMMENTS:
-//
-void CmdDebugService(int argc, char ** argv)
-{
- DWORD dwArgc;
- LPTSTR *lpszArgv;
-
-#ifdef UNICODE
- lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
- if (NULL == lpszArgv)
- {
- // CommandLineToArvW failed!!
- _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
- return;
- }
-#else
- dwArgc = (DWORD) argc;
- lpszArgv = argv;
-#endif
-
- _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
-
- SetConsoleCtrlHandler( ControlHandler, TRUE );
+ _tprintf (TEXT("DeleteService failed - %s\n"), GetLastErrorText ());
- ServiceStart( dwArgc, lpszArgv );
-
-#ifdef UNICODE
-// Must free memory allocated for arguments
-
- GlobalFree(lpszArgv);
-#endif // UNICODE
+ CloseServiceHandle (service);
+ }
+out:
+ CloseServiceHandle (svc_ctl_mgr);
+ return ret;
}
-//
-// FUNCTION: ControlHandler ( DWORD dwCtrlType )
-//
-// PURPOSE: Handled console control events
-//
-// PARAMETERS:
-// dwCtrlType - type of control event
-//
-// RETURN VALUE:
-// True - handled
-// False - unhandled
-//
-// COMMENTS:
-//
-BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
+int
+_tmain (int argc, TCHAR *argv[])
{
- switch ( dwCtrlType )
- {
- case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
- case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
- _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
- ServiceStop();
- return TRUE;
- break;
-
- }
- return FALSE;
-}
-
-//
-// FUNCTION: GetLastErrorText
-//
-// PURPOSE: copies error message text to string
-//
-// PARAMETERS:
-// lpszBuf - destination buffer
-// dwSize - size of buffer
-//
-// RETURN VALUE:
-// destination buffer
-//
-// COMMENTS:
-//
-LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
-{
- DWORD dwRet;
- LPTSTR lpszTemp = NULL;
-
- dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
- NULL,
- GetLastError(),
- LANG_NEUTRAL,
- (LPTSTR)&lpszTemp,
- 0,
- NULL );
-
- // supplied buffer is not long enough
- if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
- lpszBuf[0] = TEXT('\0');
- else
- {
- lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
- _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, (int)GetLastError() );
- }
-
- if ( lpszTemp )
- LocalFree((HLOCAL) lpszTemp );
-
- return lpszBuf;
+ SERVICE_TABLE_ENTRY dispatchTable[] = {
+ { automatic_service.name, ServiceStartAutomatic },
+ { interactive_service.name, ServiceStartInteractive },
+ { NULL, NULL }
+ };
+
+ openvpn_service[0] = automatic_service;
+ openvpn_service[1] = interactive_service;
+
+ if (argc > 1 && (*argv[1] == TEXT('-') || *argv[1] == TEXT('/')))
+ {
+ if (_tcsicmp (TEXT("install"), argv[1] + 1) == 0)
+ return CmdInstallServices ();
+ else if (_tcsicmp (TEXT("remove"), argv[1] + 1) == 0)
+ return CmdRemoveServices ();
+ else if (_tcsicmp (TEXT("start"), argv[1] + 1) == 0)
+ {
+ BOOL is_auto = argc < 3 || _tcsicmp (TEXT("interactive"), argv[2]) != 0;
+ return CmdStartService (is_auto ? automatic : interactive);
+ }
+ else
+ goto dispatch;
+
+ return 0;
+ }
+
+ /* If it doesn't match any of the above parameters
+ * the service control manager may be starting the service
+ * so we must call StartServiceCtrlDispatcher
+ */
+dispatch:
+ _tprintf (TEXT("%s -install to install the services\n"), APPNAME);
+ _tprintf (TEXT("%s -start <name> to start a service (\"automatic\" or \"interactive\")\n"), APPNAME);
+ _tprintf (TEXT("%s -remove to remove the services\n"), APPNAME);
+ _tprintf (TEXT("\nStartServiceCtrlDispatcher being called.\n"));
+ _tprintf (TEXT("This may take several seconds. Please wait.\n"));
+
+ if (!StartServiceCtrlDispatcher (dispatchTable))
+ MsgToEventLog (MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed."));
+
+ return 0;
}
diff --git a/src/openvpnserv/service.h b/src/openvpnserv/service.h
index e89a89f..94bfb07 100644
--- a/src/openvpnserv/service.h
+++ b/src/openvpnserv/service.h
@@ -1,139 +1,92 @@
-/*---------------------------------------------------------------------------
-THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
-ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
-PARTICULAR PURPOSE.
-
-Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
-
- MODULE: service.h
-
- Comments: The use of this header file and the accompanying service.c
- file simplifies the process of writting a service. You as a developer
- simply need to follow the TODO's outlined in this header file, and
- implement the ServiceStart() and ServiceStop() functions.
-
- There is no need to modify the code in service.c. Just add service.c
- to your project and link with the following libraries...
-
- libcmt.lib kernel32.lib advapi.lib shell32.lib
-
- This code also supports unicode. Be sure to compile both service.c and
- and code #include "service.h" with the same Unicode setting.
-
- Upon completion, your code will have the following command line interface
-
- <service exe> -? to display this list
- <service exe> -install to install the service
- <service exe> -remove to remove the service
- <service exe> -debug <params> to run as a console app for debugging
-
- Note: This code also implements Ctrl+C and Ctrl+Break handlers
- when using the debug option. These console events cause
- your ServiceStop routine to be called
-
- Also, this code only handles the OWN_SERVICE service type
- running in the LOCAL_SYSTEM security context.
-
- To control your service ( start, stop, etc ) you may use the
- Services control panel applet or the NET.EXE program.
-
- To aid in writing/debugging service, the
- SDK contains a utility (MSTOOLS\BIN\SC.EXE) that
- can be used to control, configure, or obtain service status.
- SC displays complete status for any service/driver
- in the service database, and allows any of the configuration
- parameters to be easily changed at the command line.
- For more information on SC.EXE, type SC at the command line.
-
-
-------------------------------------------------------------------------------*/
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2013 Heiko Hund <heiko.hund@sophos.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
#ifndef _SERVICE_H
#define _SERVICE_H
-
-#ifdef __cplusplus
-extern "C" {
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
#endif
-//////////////////////////////////////////////////////////////////////////////
-//// todo: change to desired strings
-////
-// name of the executable
-#define SZAPPNAME PACKAGE "serv"
-// internal name of the service
-#define SZSERVICENAME PACKAGE_NAME "Service"
-// displayed name of the service
-#define SZSERVICEDISPLAYNAME PACKAGE_NAME " Service"
-// list of service dependencies - "dep1\0dep2\0\0"
-#define SZDEPENDENCIES TAP_WIN_COMPONENT_ID "\0Dhcp\0\0"
-//////////////////////////////////////////////////////////////////////////////
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//// todo: ServiceStart()must be defined by in your code.
-//// The service should use ReportStatusToSCMgr to indicate
-//// progress. This routine must also be used by StartService()
-//// to report to the SCM when the service is running.
-////
-//// If a ServiceStop procedure is going to take longer than
-//// 3 seconds to execute, it should spawn a thread to
-//// execute the stop code, and return. Otherwise, the
-//// ServiceControlManager will believe that the service has
-//// stopped responding
-////
- VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
- VOID ServiceStop();
-//////////////////////////////////////////////////////////////////////////////
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//// The following are procedures which
-//// may be useful to call within the above procedures,
-//// but require no implementation by the user.
-//// They are implemented in service.c
-
-//
-// FUNCTION: ReportStatusToSCMgr()
-//
-// PURPOSE: Sets the current status of the service and
-// reports it to the Service Control Manager
-//
-// PARAMETERS:
-// dwCurrentState - the state of the service
-// dwWin32ExitCode - error code to report
-// dwWaitHint - worst case estimate to next checkpoint
-//
-// RETURN VALUE:
-// TRUE - success
-// FALSE - failure
-//
- BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
-
-
-//
-// FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
-//
-// PURPOSE: Allows any thread to log an error message
-//
-// PARAMETERS:
-// lpszMsg - text for message
-//
-// RETURN VALUE:
-// none
-//
-# define MSG_FLAGS_ERROR (1<<0)
-# define MSG_FLAGS_SYS_CODE (1<<1)
- void AddToMessageLog(DWORD flags, LPTSTR lpszMsg);
- void ResetError (void);
-//////////////////////////////////////////////////////////////////////////////
-
-
-#ifdef __cplusplus
-}
-#endif
+#include <windows.h>
+#include <stdlib.h>
+#include <tchar.h>
+
+#define APPNAME TEXT(PACKAGE "serv")
+#define SERVICE_DEPENDENCIES TAP_WIN_COMPONENT_ID "\0Dhcp\0\0"
+
+/*
+ * Message handling
+ */
+#define MSG_FLAGS_ERROR (1<<0)
+#define MSG_FLAGS_SYS_CODE (1<<1)
+#define M_INFO (0) /* informational */
+#define M_SYSERR (MSG_FLAGS_ERROR|MSG_FLAGS_SYS_CODE) /* error + system code */
+#define M_ERR (MSG_FLAGS_ERROR) /* error */
+
+typedef enum {
+ automatic,
+ interactive,
+ _service_max
+} openvpn_service_type;
+
+typedef struct {
+ openvpn_service_type type;
+ TCHAR *name;
+ TCHAR *display_name;
+ TCHAR *dependencies;
+ DWORD start_type;
+} openvpn_service_t;
+
+#define MAX_NAME 256
+typedef struct {
+ TCHAR exe_path[MAX_PATH];
+ TCHAR config_dir[MAX_PATH];
+ TCHAR ext_string[16];
+ TCHAR log_dir[MAX_PATH];
+ TCHAR ovpn_admin_group[MAX_NAME];
+ DWORD priority;
+ BOOL append;
+} settings_t;
+
+extern openvpn_service_t automatic_service;
+extern openvpn_service_t interactive_service;
+
+
+VOID WINAPI ServiceStartAutomatic (DWORD argc, LPTSTR *argv);
+VOID WINAPI ServiceStartInteractive (DWORD argc, LPTSTR *argv);
+
+int openvpn_vsntprintf (LPTSTR str, size_t size, LPCTSTR format, va_list arglist);
+int openvpn_sntprintf (LPTSTR str, size_t size, LPCTSTR format, ...);
+
+DWORD GetOpenvpnSettings (settings_t *s);
+
+BOOL ReportStatusToSCMgr (SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status);
+
+LPCTSTR GetLastErrorText ();
+DWORD MsgToEventLog (DWORD flags, LPCTSTR lpszMsg, ...);
#endif
diff --git a/src/openvpnserv/validate.c b/src/openvpnserv/validate.c
new file mode 100644
index 0000000..7458d75
--- /dev/null
+++ b/src/openvpnserv/validate.c
@@ -0,0 +1,249 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2016 Selva Nair <selva.nair@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "validate.h"
+
+#include <lmaccess.h>
+#include <shlwapi.h>
+#include <lm.h>
+
+static const WCHAR *white_list[] =
+ {
+ L"auth-retry",
+ L"config",
+ L"log",
+ L"log-append",
+ L"management",
+ L"management-forget-disconnect",
+ L"management-hold",
+ L"management-query-passwords",
+ L"management-query-proxy",
+ L"management-signal",
+ L"management-up-down",
+ L"mute",
+ L"setenv",
+ L"service",
+ L"verb",
+
+ NULL /* last value */
+ };
+
+/*
+ * Check workdir\fname is inside config_dir
+ * The logic here is simple: we may reject some valid paths if ..\ is in any of the strings
+ */
+static BOOL
+CheckConfigPath (const WCHAR *workdir, const WCHAR *fname, const settings_t *s)
+{
+ WCHAR tmp[MAX_PATH];
+ const WCHAR *config_file = NULL;
+ const WCHAR *config_dir = NULL;
+
+ /* convert fname to full path */
+ if (PathIsRelativeW (fname) )
+ {
+ snwprintf (tmp, _countof(tmp), L"%s\\%s", workdir, fname);
+ tmp[_countof(tmp)-1] = L'\0';
+ config_file = tmp;
+ }
+ else
+ {
+ config_file = fname;
+ }
+
+#ifdef UNICODE
+ config_dir = s->config_dir;
+#else
+ if (MultiByteToWideChar (CP_UTF8, 0, s->config_dir, -1, widepath, MAX_PATH) == 0)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("Failed to convert config_dir name to WideChar"));
+ return FALSE;
+ }
+ config_dir = widepath;
+#endif
+
+ if (wcsncmp (config_dir, config_file, wcslen(config_dir)) == 0 &&
+ wcsstr (config_file + wcslen(config_dir), L"..") == NULL )
+ return TRUE;
+
+ return FALSE;
+}
+
+
+/*
+ * A simple linear search meant for a small wchar_t *array.
+ * Returns index to the item if found, -1 otherwise.
+ */
+static int
+OptionLookup (const WCHAR *name, const WCHAR *white_list[])
+{
+ int i;
+
+ for (i = 0 ; white_list[i]; i++)
+ {
+ if ( wcscmp(white_list[i], name) == 0 )
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * The Administrators group may be localized or renamed by admins.
+ * Get the local name of the group using the SID.
+ */
+static BOOL
+GetBuiltinAdminGroupName (WCHAR *name, DWORD nlen)
+{
+ BOOL b = FALSE;
+ PSID admin_sid = NULL;
+ DWORD sid_size = SECURITY_MAX_SID_SIZE;
+ SID_NAME_USE snu;
+
+ WCHAR domain[MAX_NAME];
+ DWORD dlen = _countof(domain);
+
+ admin_sid = malloc(sid_size);
+ if (!admin_sid)
+ return FALSE;
+
+ b = CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, admin_sid, &sid_size);
+ if(b)
+ {
+ b = LookupAccountSidW(NULL, admin_sid, name, &nlen, domain, &dlen, &snu);
+ }
+
+ free (admin_sid);
+
+ return b;
+}
+
+/*
+ * Check whether user is a member of Administrators group or
+ * the group specified in s->ovpn_admin_group
+ */
+BOOL
+IsAuthorizedUser (SID *sid, settings_t *s)
+{
+ LOCALGROUP_USERS_INFO_0 *groups = NULL;
+ DWORD nread;
+ DWORD nmax;
+ WCHAR *tmp = NULL;
+ const WCHAR *admin_group[2];
+ WCHAR username[MAX_NAME];
+ WCHAR domain[MAX_NAME];
+ WCHAR sysadmin_group[MAX_NAME];
+ DWORD err, len = MAX_NAME;
+ int i;
+ BOOL ret = FALSE;
+ SID_NAME_USE sid_type;
+
+ /* Get username */
+ if (!LookupAccountSidW (NULL, sid, username, &len, domain, &len, &sid_type))
+ {
+ MsgToEventLog (M_SYSERR, TEXT("LookupAccountSid"));
+ goto out;
+ }
+
+ /* Get an array of groups the user is member of */
+ err = NetUserGetLocalGroups (NULL, username, 0, LG_INCLUDE_INDIRECT, (LPBYTE *) &groups,
+ MAX_PREFERRED_LENGTH, &nread, &nmax);
+ if (err && err != ERROR_MORE_DATA)
+ {
+ SetLastError (err);
+ MsgToEventLog (M_SYSERR, TEXT("NetUserGetLocalGroups"));
+ goto out;
+ }
+
+ if (GetBuiltinAdminGroupName(sysadmin_group, _countof(sysadmin_group)))
+ {
+ admin_group[0] = sysadmin_group;
+ }
+ else
+ {
+ MsgToEventLog (M_SYSERR, TEXT("Failed to get the name of Administrators group. Using the default."));
+ /* use the default value */
+ admin_group[0] = SYSTEM_ADMIN_GROUP;
+ }
+
+#ifdef UNICODE
+ admin_group[1] = s->ovpn_admin_group;
+#else
+ tmp = NULL;
+ len = MultiByteToWideChar (CP_UTF8, 0, s->ovpn_admin_group, -1, NULL, 0);
+ if (len == 0 || (tmp = malloc (len*sizeof(WCHAR))) == NULL)
+ {
+ MsgToEventLog (M_SYSERR, TEXT("Failed to convert admin group name to WideChar"));
+ goto out;
+ }
+ MultiByteToWideChar (CP_UTF8, 0, s->ovpn_admin_group, -1, tmp, len);
+ admin_group[1] = tmp;
+#endif
+
+ /* Check if user's groups include any of the admin groups */
+ for (i = 0; i < nread; i++)
+ {
+ if ( wcscmp (groups[i].lgrui0_name, admin_group[0]) == 0 ||
+ wcscmp (groups[i].lgrui0_name, admin_group[1]) == 0
+ )
+ {
+ MsgToEventLog (M_INFO, TEXT("Authorizing user %s by virtue of membership in group %s"),
+ username, groups[i].lgrui0_name);
+ ret = TRUE;
+ break;
+ }
+ }
+
+out:
+ if (groups)
+ NetApiBufferFree (groups);
+ free (tmp);
+
+ return ret;
+}
+
+/*
+ * Check whether option argv[0] is white-listed. If argv[0] == "--config",
+ * also check that argv[1], if present, passes CheckConfigPath().
+ * The caller should set argc to the number of valid elements in argv[] array.
+ */
+BOOL
+CheckOption (const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s)
+{
+ /* Do not modify argv or *argv -- ideally it should be const WCHAR *const *, but alas...*/
+
+ if ( wcscmp (argv[0], L"--config") == 0 &&
+ argc > 1 &&
+ !CheckConfigPath (workdir, argv[1], s)
+ )
+ {
+ return FALSE;
+ }
+
+ /* option name starts at 2 characters from argv[i] */
+ if (OptionLookup (argv[0] + 2, white_list) == -1) /* not found */
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/openvpnserv/validate.h b/src/openvpnserv/validate.h
new file mode 100644
index 0000000..0d0270a
--- /dev/null
+++ b/src/openvpnserv/validate.h
@@ -0,0 +1,48 @@
+
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2016 Selva Nair <selva.nair@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef VALIDATE_H
+#define VALIDATE_H
+
+#include "service.h"
+
+/* Authorized groups who can use any options and config locations */
+#define SYSTEM_ADMIN_GROUP TEXT("Administrators")
+#define OVPN_ADMIN_GROUP TEXT("OpenVPN Administrators")
+/* The last one may be reset in registry: HKLM\Software\OpenVPN\ovpn_admin_group */
+
+BOOL
+IsAuthorizedUser (SID *sid, settings_t *s);
+
+BOOL
+CheckOption (const WCHAR *workdir, int narg, WCHAR *argv[], const settings_t *s);
+
+static inline BOOL
+IsOption (const WCHAR *o)
+{
+ return (wcsncmp (o, L"--", 2) == 0);
+}
+
+#endif