diff --git a/CMakeLists.txt b/CMakeLists.txt index fa02acb..e225651 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if(NOT VCPKG_MANIFEST_DIR) set(VCPKG_MANIFEST_DIR ${CMAKE_SOURCE_DIR}/vcpkg_manifests/openssl_3) endif() -project(openvpn-gui C) +project(openvpn-gui C CXX) add_executable(${PROJECT_NAME} WIN32 access.c @@ -139,10 +139,33 @@ target_compile_definitions(${PROJECT_NAME_PLAP} PRIVATE WIN32_LEAN_AND_MEAN HAVE_CONFIG_H) +if (NOT TEST_PLAP_EXE) + set(TEST_PLAP_EXE "test_plap") +endif() + +add_executable(${TEST_PLAP_EXE} + plap/test_plap.cpp + plap/plap_common.c) + +target_link_libraries(${TEST_PLAP_EXE} PRIVATE + Rpcrt4 + Ole32 + Gdi32) + +target_include_directories(${TEST_PLAP_EXE} PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}) +target_compile_definitions(${TEST_PLAP_EXE} PRIVATE + _UNICODE + UNICODE + WIN32_LEAN_AND_MEAN + HAVE_CONFIG_H) + if(MSVC) # work around msvc generator Debug/Release directory ugliness, doesn't apply for Ninja - set_target_properties(${PROJECT_NAME} ${PROJECT_NAME_PLAP} PROPERTIES + set_target_properties(${PROJECT_NAME} ${PROJECT_NAME_PLAP} ${TEST_PLAP_EXE} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<0:> LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<0:> ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<0:>) endif(MSVC) + +set_target_properties(${TEST_PLAP_EXE} PROPERTIES + LINK_FLAGS " /MANIFEST:EMBED /MANIFESTINPUT:${CMAKE_SOURCE_DIR}/plap/test-plap.manifest ") diff --git a/configure.ac b/configure.ac index 986a5c0..bb1705d 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,7 @@ AM_INIT_AUTOMAKE([subdir-objects]) AC_CANONICAL_HOST AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC_C99 +AC_PROG_CXX AC_CHECK_TOOL([WINDRES], [windres]) LT_INIT([win32-dll]) LT_LANG([Windows Resource]) diff --git a/plap/Makefile.am b/plap/Makefile.am index 9a215ab..311b6ac 100644 --- a/plap/Makefile.am +++ b/plap/Makefile.am @@ -30,9 +30,13 @@ LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) MAINTAINERCLEANFILES = \ $(srcdir)/Makefile.in $(srcdir)/credentialprovider.h +#if ENABLE_OPENVPN_PLAP_TEST +bin_PROGRAMS = test_plap +#endif lib_LTLIBRARIES = libopenvpn_plap.la libopenvpn_plap_la_CFLAGS = -DDISABLE_PASSWORD_CHANGE -D_UNICODE -municode +test_plap_CXXFLAGS = -DDEBUG -D_UNICODE -municode libopenvpn_plap_la_RESOURCES = \ $(top_srcdir)/res/openvpn-gui-res-cs.rc \ @@ -101,3 +105,7 @@ libopenvpn_plap_la_LDFLAGS = -no-undefined -avoid-version -static-libgcc credentialprovider.h: wget https://raw.githubusercontent.com/mirror/mingw-w64/master/mingw-w64-headers/include/credentialprovider.h + +test_plap_SOURCES = test_plap.cpp test-plap-res.rc test-plap.manifest plap_common.c credentialprovider.h +test_plap_LDFLAGS = -static-libgcc -static-libstdc++ +test_plap_LDADD = -lrpcrt4 -lole32 -lgdi32 -lcomctl32 diff --git a/plap/test-plap-res.rc b/plap/test-plap-res.rc new file mode 100644 index 0000000..7b89241 --- /dev/null +++ b/plap/test-plap-res.rc @@ -0,0 +1,23 @@ +/* + * OpenVPN-GUI -- A Windows GUI for OpenVPN. + * + * Copyright (C) 2009 Heiko Hund + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (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 + */ + +/* Manifest for visual styles */ +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "test-plap.manifest" diff --git a/plap/test-plap.manifest b/plap/test-plap.manifest new file mode 100644 index 0000000..5c68f9b --- /dev/null +++ b/plap/test-plap.manifest @@ -0,0 +1,56 @@ + + + +OpenVPN GUI PLAP DLL Test Program + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/PM + + + PerMonitorV2, PerMonitor + + + diff --git a/plap/test_plap.cpp b/plap/test_plap.cpp new file mode 100644 index 0000000..fbdf921 --- /dev/null +++ b/plap/test_plap.cpp @@ -0,0 +1,177 @@ +/* + * OpenVPN-PLAP-Provider + * + * Copyright (C) 2019-2022 Selva Nair + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (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 + */ + +#undef WIN32_LEAN_AND_MEAN + +#include +#include "plap_common.h" +#include +#include +#include +#include +#include +#include +#include + +#define error_return(fname, hr) do { fwprintf(stdout, L"Error in %ls: status = 0x%08x", fname, hr);\ + return 1;} while (0) +typedef HRESULT (WINAPI * f_func)(REFCLSID rclsid, REFIID riid, LPVOID* ppv); + +class MyQueryContinue : public IQueryContinueWithStatus +{ + public: + STDMETHODIMP_(ULONG) AddRef() { + return InterlockedIncrement(&ref_count); + } + STDMETHODIMP_(ULONG) Release() { + int count = InterlockedDecrement(&ref_count); + if (ref_count == 0) delete this; + return count; + } + STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { return E_FAIL; } + STDMETHODIMP QueryContinue() {return time(NULL) > timeout ? S_FALSE : S_OK;} + STDMETHODIMP SetStatusMessage(const wchar_t *ws) { wprintf(L"%ls\r", ws); return S_OK; } + MyQueryContinue() : ref_count(1) {}; + time_t timeout; + + private: + ~MyQueryContinue()=default; + LONG ref_count; +}; + +static int test_provider(IClassFactory *cf) +{ + assert(cf != NULL); + ICredentialProvider *o = NULL; + HRESULT hr; + + hr = cf->CreateInstance(NULL, IID_ICredentialProvider, (void**) &o); + + if (!SUCCEEDED(hr)) error_return(L"IID_ICredentialProvider", hr); + + hr = o->SetUsageScenario(CPUS_PLAP, 0); + if (!SUCCEEDED(hr)) error_return(L"SetUsageScenario", hr); + + DWORD count, def; + BOOL auto_def; + hr = o->GetCredentialCount(&count, &def, &auto_def); + if (!SUCCEEDED(hr)) error_return(L"GetCredentialCount", hr); + + fwprintf(stdout, L"credential count = %lu, default = %d, autologon = %d\n", count, (int) def, auto_def); + if (count < 1) fwprintf(stdout, L"No persistent configs found!\n"); + + ICredentialProviderCredential *c = NULL; + MyQueryContinue *qc = new MyQueryContinue(); + for (DWORD i = 0; i < count; i++) + { + hr = o->GetCredentialAt(i, &c); + if (!SUCCEEDED(hr)) error_return(L"GetCredentialAt", hr); + + fwprintf(stdout, L"credential # = %lu: ", i); + wchar_t *ws; + + for (DWORD j = 0; j < 4; j++) + { + hr = c->GetStringValue(j, &ws); + if (!SUCCEEDED(hr)) error_return(L"GetStringValue", hr); + CoTaskMemFree(ws); + } + + /* test getbitmap */ + HBITMAP bmp; + hr = c->GetBitmapValue(0, &bmp); + if (!SUCCEEDED(hr)) + fwprintf(stdout, L"Warning: could not get bitmap"); /* not fatal */ + else + DeleteObject(bmp); + + /* set a time out so that we can move to next config in case */ + qc->timeout = time(NULL) + 20; + + /* get a connection instance and call connect on it */ + IConnectableCredentialProviderCredential *c1 = NULL; + hr = c->QueryInterface(IID_IConnectableCredentialProviderCredential, (void**)&c1); + + fwprintf(stdout, L"\nConnecting connection # <%lu>\n", i); + c1->Connect(qc); /* this will return when connected/failed or qc timesout */ + + fwprintf(stdout, L"\nsleep for 2 sec\n"); + Sleep(2000); + c1->Release(); + } + + assert(o->Release() == 0); /* check refcount */ + assert(qc->Release() == 0); + return 0; +} + +int wmain() +{ + HRESULT hr; + _setmode(_fileno(stdout), _O_U16TEXT); + _setmode(_fileno(stderr), _O_U16TEXT); + + IClassFactory *cf = NULL; + DWORD ctx = CLSCTX_INPROC_SERVER; + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (!SUCCEEDED(hr)) error_return(L"CoIntialize", hr); + + /* Test by loading the dll */ + fwprintf(stdout, L"Test plap dll direct loading\n"); + + HMODULE lib = LoadLibraryW(L"C:\\Program Files\\OpenVPN\\bin\\libopenvpn_plap.dll"); + f_func func = NULL; + if (lib == NULL) + { + fwprintf(stderr, L"Failed to load the dll: error = 0x%08x\n", GetLastError()); + } + else { + func = (f_func) GetProcAddress(lib, "DllGetClassObject"); + if (!func) + fwprintf(stderr, L"Failed to find DllGetClassObject in dll: error = 0x%08x\n", GetLastError()); + } + if (func) { + hr = func(CLSID_OpenVPNProvider, IID_IClassFactory, (void **)&cf); + if (!SUCCEEDED(hr)) fwprintf(stdout, L"Error in DllGetClassObject: status = 0x%08x\n", hr); + else { + fwprintf(stdout, L"Success: found ovpn provider class factory by direct access\n"); + cf->Release(); + } + } + + /* Test by finding the class through COM's registration mechanism */ + fwprintf(stdout, L"Testing plap using CoGetclassobject -- requires proper dll registration\n"); + + hr = CoGetClassObject(CLSID_OpenVPNProvider, ctx, NULL, IID_IClassFactory, (void **)&cf); + if (SUCCEEDED(hr)) { + test_provider(cf); + cf->Release(); + } + else { + fwprintf(stdout, L"CoGetClassObject (class not registered?): error = 0x%08x\n", hr); + } + + CoUninitialize(); + if (lib) { + FreeLibrary(lib); + } + return 0; +}