Implement Pre-Logon Access Provider for start before logon

- COM interfaces for ICredentialProvider and
  IConnectableCredentialProviderCredential combined
  with a trimmed down user-interface implemented as
  libopenvpn_plap.dll

- Connections autostarted by OpenVPNService are enumerated
  as possible PLAP connections. The user is expected to leave
  these in management hold so that "connect" will popup any
  required user dialogs.

To use:
 - Register the dll as a PLAP provider (see included .reg files)
 - The enumerated connections will show up as tiles in the PLAP
   screen of the login desktop (secure desktop).

Signed-off-by: Selva Nair <selva.nair@gmail.com>
pull/529/head
Selva Nair 2022-07-10 23:39:09 -04:00
parent b828e763ad
commit a500b9553e
17 changed files with 2459 additions and 2 deletions

View File

@ -32,6 +32,7 @@ MAINTAINERCLEANFILES = \
$(srcdir)/config.guess $(srcdir)/config.sub \
$(srcdir)/compile
SUBDIRS = . plap
bin_PROGRAMS = openvpn-gui
dist_doc_DATA = \

View File

@ -29,11 +29,13 @@ AC_DEFINE([CORE_COPYRIGHT_YEAR_END], ["2020"], [Last copyright year for daemon i
AC_CONFIG_AUX_DIR([.])
AM_CONFIG_HEADER([config.h])
AC_CONFIG_SRCDIR([main.h])
AM_INIT_AUTOMAKE
AM_INIT_AUTOMAKE([subdir-objects])
AC_CANONICAL_HOST
AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC_C99
AC_CHECK_TOOL([WINDRES], [windres])
LT_INIT([win32-dll])
LT_LANG([Windows Resource])
AC_ARG_ENABLE(
[distonly],
@ -102,5 +104,5 @@ fi
test "${GCC}" = "yes" && CFLAGS="${CFLAGS} -pedantic -Wall -Wextra"
AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([Makefile plap/Makefile])
AC_OUTPUT

98
plap/Makefile.am Normal file
View File

@ -0,0 +1,98 @@
# OpenVPN-PLAP-Provider
#
# Copyright (C) 2021-2022 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 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
RCCOMPILE = $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS)
LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE)
.rc.lo:
$(LTRCCOMPILE) -i "$<" -o "$@"
.rc.o:
$(RCCOMPILE) -i "$<" -o "$@"
MAINTAINERCLEANFILES = \
$(srcdir)/Makefile.in
lib_LTLIBRARIES = libopenvpn_plap.la
libopenvpn_plap_la_CFLAGS = -DDISABLE_PASSWORD_CHANGE -D_UNICODE -municode
libopenvpn_plap_la_RESOURCES = \
$(top_srcdir)/res/openvpn-gui-res-cs.rc \
$(top_srcdir)/res/openvpn-gui-res-de.rc \
$(top_srcdir)/res/openvpn-gui-res-dk.rc \
$(top_srcdir)/res/openvpn-gui-res-en.rc \
$(top_srcdir)/res/openvpn-gui-res-es.rc \
$(top_srcdir)/res/openvpn-gui-res-fa.rc \
$(top_srcdir)/res/openvpn-gui-res-fi.rc \
$(top_srcdir)/res/openvpn-gui-res-fr.rc \
$(top_srcdir)/res/openvpn-gui-res-it.rc \
$(top_srcdir)/res/openvpn-gui-res-jp.rc \
$(top_srcdir)/res/openvpn-gui-res-kr.rc \
$(top_srcdir)/res/openvpn-gui-res-nl.rc \
$(top_srcdir)/res/openvpn-gui-res-no.rc \
$(top_srcdir)/res/openvpn-gui-res-pl.rc \
$(top_srcdir)/res/openvpn-gui-res-pt.rc \
$(top_srcdir)/res/openvpn-gui-res-ru.rc \
$(top_srcdir)/res/openvpn-gui-res-se.rc \
$(top_srcdir)/res/openvpn-gui-res-tr.rc \
$(top_srcdir)/res/openvpn-gui-res-ua.rc \
$(top_srcdir)/res/openvpn-gui-res-zh-hans.rc \
$(top_srcdir)/res/openvpn-gui-res-zh-hant.rc \
$(top_srcdir)/res/connected.ico \
$(top_srcdir)/res/connecting.ico \
$(top_srcdir)/res/disconnected.ico \
$(top_srcdir)/res/openvpn-gui.ico \
$(top_srcdir)/res/reconnecting.ico \
openvpn-plap-res.rc \
openvpn-plap.manifest
openvpn-plap-res.lo: $(libopenvpn_plap_la_RESOURCES) $(top_srcdir)/openvpn-gui-res.h
EXTRA_DIST = openvpn-plap-res.rc openvpn-plap.manifest
libopenvpn_plap_la_SOURCES = \
stub.c plap_provider.c \
plap_connection.h plap_connection.c \
plap_common.h plap_common.c \
plap_dll.h plap_dll.c \
ui_glue.h ui_glue.c \
$(top_srcdir)/openvpn.c \
$(top_srcdir)/localization.c\
$(top_srcdir)/options.c \
$(top_srcdir)/proxy.c \
$(top_srcdir)/registry.c \
$(top_srcdir)/manage.c \
$(top_srcdir)/misc.c \
$(top_srcdir)/openvpn_config.c \
$(top_srcdir)/config_parser.c \
$(top_srcdir)/pkcs11.c \
openvpn-plap-res.rc
libopenvpn_plap_la_LIBADD = \
-lws2_32 \
-lcomctl32 \
-lwinhttp \
-lcrypt32 \
-lole32 \
-lshlwapi \
-lgdi32 \
-lrpcrt4
libopenvpn_plap_la_LDFLAGS = -no-undefined -avoid-version -static-libgcc

View File

@ -0,0 +1,11 @@
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\PLAP Providers\{4fbb8b67-cf02-4982-a7a8-3dd06a2c2ebd}]
@="OpenVPNProvider"
[HKEY_CLASSES_ROOT\CLSID\{4fbb8b67-cf02-4982-a7a8-3dd06a2c2ebd}]
@="OpenVPNProvider"
[HKEY_CLASSES_ROOT\CLSID\{4fbb8b67-cf02-4982-a7a8-3dd06a2c2ebd}\InprocServer32]
@="C:\\Program Files\\OpenVPN\\bin\\libopenvpn_plap.dll"
"ThreadingModel"="Apartment"

117
plap/openvpn-plap-res.rc Normal file
View File

@ -0,0 +1,117 @@
/*
* OpenVPN-GUI -- A Windows GUI for OpenVPN.
*
* Copyright (C) 2009 Heiko Hund <heikoh@users.sf.net>
*
* 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <winresrc.h>
#include <richedit.h>
#include "../openvpn-gui-res.h"
/* Language resource files are UTF-8 encoded */
#pragma code_page(65001)
/* Manifest for version 6 common controls */
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "openvpn-plap.manifest"
/* Application Icons */
ID_ICO_APP ICON DISCARDABLE "../res/openvpn-gui.ico"
ID_ICO_CONNECTED ICON DISCARDABLE "../res/connected.ico"
ID_ICO_CONNECTING ICON DISCARDABLE "../res/connecting.ico"
ID_ICO_DISCONNECTED ICON DISCARDABLE "../res/disconnected.ico"
#ifdef ENABLE_OVPN3
#define ADVANCED_DIALOG_HEIGHT 320
#else
#define ADVANCED_DIALOG_HEIGHT 280
#endif
#include "../res/openvpn-gui-res-cs.rc"
#include "../res/openvpn-gui-res-de.rc"
#include "../res/openvpn-gui-res-dk.rc"
#include "../res/openvpn-gui-res-en.rc"
#include "../res/openvpn-gui-res-es.rc"
#include "../res/openvpn-gui-res-fa.rc"
#include "../res/openvpn-gui-res-fi.rc"
#include "../res/openvpn-gui-res-fr.rc"
#include "../res/openvpn-gui-res-it.rc"
#include "../res/openvpn-gui-res-jp.rc"
#include "../res/openvpn-gui-res-kr.rc"
#include "../res/openvpn-gui-res-nl.rc"
#include "../res/openvpn-gui-res-no.rc"
#include "../res/openvpn-gui-res-pl.rc"
#include "../res/openvpn-gui-res-pt.rc"
#include "../res/openvpn-gui-res-ru.rc"
#include "../res/openvpn-gui-res-se.rc"
#include "../res/openvpn-gui-res-tr.rc"
#include "../res/openvpn-gui-res-ua.rc"
#include "../res/openvpn-gui-res-zh-hans.rc"
#include "../res/openvpn-gui-res-zh-hant.rc"
/* Version information and such */
VS_VERSION_INFO VERSIONINFO
FILEVERSION PACKAGE_VERSION_RESOURCE
PRODUCTVERSION PACKAGE_VERSION_RESOURCE
FILEOS VOS_NT
FILETYPE VFT_DLL
BEGIN
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation"
, LANG_CZECH, 1200
, LANG_GERMAN, 1200
, LANG_DANISH, 1200
, LANG_ENGLISH, 1200
, LANG_SPANISH, 1200
, LANG_FARSI, 1200
, LANG_FINNISH, 1200
, LANG_FRENCH, 1200
, LANG_ITALIAN, 1200
, LANG_JAPANESE, 1200
, LANG_DUTCH, 1200
, LANG_NORWEGIAN, 1200
, LANG_POLISH, 1200
, LANG_PORTUGUESE, 1200
, LANG_RUSSIAN, 1200
, LANG_SWEDISH, 1200
, LANG_TURKISH, 1200
, LANG_UKRAINIAN, 1200
, LANG_CHINESE, 1200
, LANG_KOREAN, 1200
END
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0" /* en_US, Unicode */
BEGIN
VALUE "FileDescription", "OpenVPN PLAP for Windows"
VALUE "FileVersion", PACKAGE_VERSION_RESOURCE_STR
VALUE "InternalName", "openvpn-plap"
VALUE "OriginalFilename", "libopenvpn_plap.dll"
VALUE "ProductName", "OpenVPN PLAP"
VALUE "ProductVersion", PACKAGE_VERSION_RESOURCE_STR
VALUE "Website", PACKAGE_URL
VALUE "LegalCopyright", "Selva Nair <selva.nair@gmail.com> and other " PACKAGE_NAME " developers"
VALUE "CompanyName", "OpenVPN Community"
END
END
END

View File

@ -0,0 +1,5 @@
Windows Registry Editor Version 5.00
[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\PLAP Providers\{4fbb8b67-cf02-4982-a7a8-3dd06a2c2ebd}]
[-HKEY_CLASSES_ROOT\CLSID\{4fbb8b67-cf02-4982-a7a8-3dd06a2c2ebd}]

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<assemblyIdentity
name="OpenVPN.GUI.PLAP"
version="1.0.0.0"
type="win32"/>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"/>
</dependentAssembly>
</dependency>
</assembly>

101
plap/plap_common.c Normal file
View File

@ -0,0 +1,101 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "plap_common.h"
#include <windows.h>
#include <shlwapi.h>
#include <stdio.h>
#include <time.h>
#include "localization.h"
static FILE *fp;
static CRITICAL_SECTION log_write;
void
init_debug()
{
if (!fp)
fp = _wfopen(L"C:\\Windows\\Temp\\openvpn-plap-debug.txt", L"a+");
InitializeCriticalSection(&log_write);
}
void
uninit_debug()
{
if (fp)
fclose(fp);
DeleteCriticalSection(&log_write);
}
void
x_dmsg(const char *file, const char *func, const wchar_t *fmt, ...)
{
wchar_t buf[1024];
if (!fp)
{
return;
}
va_list args;
va_start (args, fmt);
vswprintf(buf, _countof(buf), fmt, args);
va_end(args);
wchar_t date[30];
time_t log_time = time(NULL);
struct tm *time_struct = localtime(&log_time);
_snwprintf(date, _countof(date), L"%d-%.2d-%.2d %.2d:%.2d:%.2d",
time_struct->tm_year + 1900,
time_struct->tm_mon + 1,
time_struct->tm_mday,
time_struct->tm_hour,
time_struct->tm_min,
time_struct->tm_sec);
EnterCriticalSection(&log_write);
if (file && func)
{
fwprintf(fp, L"%ls %hs:%hs: %ls\n", date, file, func, buf);
}
else
{
fwprintf(fp, L"%ls %ls\n", date, buf);
}
fflush(fp);
LeaveCriticalSection(&log_write);
}
void
debug_print_guid(const GUID *riid, const wchar_t *context)
{
RPC_CSTR str = NULL;
if (UuidToStringA((GUID*) riid, &str) == RPC_S_OK)
{
x_dmsg(NULL, NULL, L"%ls %hs", context, str);
RpcStringFreeA(&str);
}
}

69
plap/plap_common.h Normal file
View File

@ -0,0 +1,69 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifndef PLAP_HELPER_H
#define PLAP_HELPER_H
#include <winsock2.h> /* avoids warning about winsock2 not included first */
#include <windows.h>
#ifdef __GNUC__
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DEBUG
#define dmsg(fmt, ...) x_dmsg(__FILE__, __func__, fmt, ##__VA_ARGS__)
#else
#define dmsg(...) do { ; } while (0)
#endif
void x_dmsg(const char *file, const char *func, const wchar_t *fmt, ...);
void init_debug();
void uninit_debug();
void debug_print_guid(const GUID *riid, const wchar_t *context);
/* Shortcuts for cumbersome calls to COM methods of an object through its v-table */
#define ADDREF(p) (p)->lpVtbl->AddRef(p)
#define RELEASE(p) (p)->lpVtbl->Release(p)
#define QUERY_INTERFACE(p, riid, ppv) (p)->lpVtbl->QueryInterface(p, riid, ppv)
#ifdef __cplusplus
}
#endif
#endif /* PLAP_HELPER_H */
/* GUID for a OpenVPNProvider -- created using uuidgen -r
* The following must be outside any include guards. Include <initguid.h>
* before including this file in one location where this GUID has to be created.
* In other places this will get defined as an extern.
*/
#ifdef DEFINE_GUID
DEFINE_GUID(CLSID_OpenVPNProvider, 0x4fbb8b67, 0xcf02, 0x4982, 0xa7, 0xa8,
0x3d, 0xd0, 0x6a, 0x2c, 0x2e, 0xbd);
#endif

598
plap/plap_connection.c Normal file
View File

@ -0,0 +1,598 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "plap_common.h"
#include "plap_connection.h"
#include "plap_dll.h"
#include <assert.h>
/* A "class" that implements IConnectableCredentialProviderCredential */
struct OpenVPNConnection
{
const IConnectableCredentialProviderCredentialVtbl *lpVtbl; /* base class vtable */
/* "private" members */
ICredentialProviderCredentialEvents *events; /* Passed in by Logon UI for callbacks */
connection_t *c; /* GUI connection data */
const wchar_t *display_name;
IQueryContinueWithStatus *qc; /* Passed in by LogonUI for checking status of connect */
BOOL connect_cancelled; /* we set this true if user cancels the connect operation */
LONG ref_count;
};
#define ICCPC IConnectableCredentialProviderCredential /* shorthand for a long name */
#define ISCONNECTED(c) (ConnectionState(c) == state_connected)
#define ISDISCONNECTED(c) (ConnectionState(c) == state_disconnected)
#define ISONHOLD(c) (ConnectionState(c) == state_onhold)
/* Methods in IConnectableCredentialProviderCredential that we need to define */
/* IUnknown */
static HRESULT WINAPI QueryInterface(ICCPC *this, REFIID riid, void** ppv);
static ULONG WINAPI AddRef(ICCPC *this);
static ULONG WINAPI Release(ICCPC *this);
/* ICredentialProviderCredential */
static HRESULT WINAPI Advise(ICCPC *this, ICredentialProviderCredentialEvents *e);
static HRESULT WINAPI UnAdvise(ICCPC *this);
static HRESULT WINAPI SetSelected(ICCPC *this, BOOL *auto_logon);
static HRESULT WINAPI SetDeselected(ICCPC *this);
static HRESULT WINAPI GetFieldState(ICCPC *this, DWORD field, CREDENTIAL_PROVIDER_FIELD_STATE *fs,
CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE *fis);
static HRESULT WINAPI GetStringValue(ICCPC *this, DWORD index, WCHAR **ws);
static HRESULT WINAPI GetBitmapValue(ICCPC *this, DWORD field, HBITMAP *bmp);
static HRESULT WINAPI GetSubmitButtonValue(ICCPC *this, DWORD field, DWORD *adjacent);
static HRESULT WINAPI SetStringValue(ICCPC *this, DWORD field, const WCHAR *ws);
static HRESULT WINAPI GetCheckboxValue(ICCPC *this, DWORD field, BOOL *checked, wchar_t **label);
static HRESULT WINAPI GetComboBoxValueCount(ICCPC *this, DWORD field, DWORD *items, DWORD *selected_item);
static HRESULT WINAPI GetComboBoxValueAt(ICCPC *this, DWORD field, DWORD item, wchar_t **item_value);
static HRESULT WINAPI SetCheckboxValue(ICCPC *this, DWORD field, BOOL checked);
static HRESULT WINAPI SetComboBoxSelectedValue(ICCPC *this, DWORD field, DWORD selected_item);
static HRESULT WINAPI CommandLinkClicked(ICCPC *this, DWORD field);
static HRESULT WINAPI GetSerialization(ICCPC *this,
CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *response,
CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *cs, wchar_t **text,
CREDENTIAL_PROVIDER_STATUS_ICON *icon);
static HRESULT WINAPI ReportResult(ICCPC *this, NTSTATUS status, NTSTATUS substatus,
wchar_t **status_text, CREDENTIAL_PROVIDER_STATUS_ICON *icon);
/* IConnectableCredentialProviderCredential */
static HRESULT WINAPI Connect(ICCPC *this, IQueryContinueWithStatus *qc);
static HRESULT WINAPI Disconnect(ICCPC *this);
/* use a static table for filling in the methods */
#define M_(x) .x = x
static const IConnectableCredentialProviderCredentialVtbl iccpc_vtbl = {
M_(QueryInterface),
M_(AddRef),
M_(Release),
M_(Advise),
M_(UnAdvise),
M_(SetSelected),
M_(SetDeselected),
M_(GetFieldState),
M_(GetStringValue),
M_(GetBitmapValue),
M_(GetCheckboxValue),
M_(GetSubmitButtonValue),
M_(GetComboBoxValueCount),
M_(GetComboBoxValueAt),
M_(SetStringValue),
M_(SetCheckboxValue),
M_(SetComboBoxSelectedValue),
M_(CommandLinkClicked),
M_(GetSerialization),
M_(ReportResult),
M_(Connect),
M_(Disconnect),
};
/* constructor */
OpenVPNConnection *
OpenVPNConnection_new()
{
dmsg(L"Entry");
OpenVPNConnection *oc = calloc(sizeof(*oc), 1);
if (oc)
{
oc->lpVtbl = &iccpc_vtbl;
oc->ref_count = 1;
dll_addref();
}
return oc;
}
/* destructor */
static void
OpenVPNConnection_free(OpenVPNConnection *oc)
{
dmsg(L"Entry: profile: <%ls>", oc->display_name);
free(oc);
dll_release();
}
static ULONG WINAPI
AddRef(ICCPC *this)
{
OpenVPNConnection *oc = (OpenVPNConnection *) this;
dmsg(L"Connection: ref_count after increment = %lu", oc->ref_count + 1);
return InterlockedIncrement(&oc->ref_count);
}
static ULONG WINAPI
Release(ICCPC *this)
{
OpenVPNConnection *oc = (OpenVPNConnection *) this;
int count = InterlockedDecrement(&oc->ref_count);
dmsg(L"Connection: ref_count after decrement = %lu", count);
if (count == 0)
{
OpenVPNConnection_free(oc); /* delete self */
}
return count;
}
static HRESULT WINAPI
QueryInterface(ICCPC *this, REFIID riid, void** ppv)
{
HRESULT hr;
#ifdef DEBUG
debug_print_guid(riid, L"In OVPN Connection QueryInterface with riid = ");
#endif
if (ppv != NULL)
{
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_ICredentialProviderCredential)
|| IsEqualIID(riid, &IID_IConnectableCredentialProviderCredential))
{
*ppv = this;
ADDREF(this);
hr = S_OK;
}
else
{
*ppv = NULL;
hr = E_NOINTERFACE;
}
}
else
{
hr = E_POINTER;
}
return hr;
}
/*
* LogonUI calls Advise first and the passed in events may be used for
* making callbacks to notify changes asynchronously
*/
static HRESULT
WINAPI Advise(ICCPC *this, ICredentialProviderCredentialEvents *e)
{
HWND hwnd;
dmsg(L"Entry");
OpenVPNConnection *oc = (OpenVPNConnection *) this;
if (oc->events)
RELEASE(oc->events);
oc->events = e;
ADDREF(e);
if (e
&& e->lpVtbl->OnCreatingWindow(e, &hwnd) == S_OK)
{
dmsg(L"Setting hwnd");
SetParentWindow(hwnd);
}
return S_OK;
}
/* We keep a copy of events pointer in Advise, release it here */
static HRESULT WINAPI
UnAdvise(ICCPC *this)
{
dmsg(L"Entry");
OpenVPNConnection *oc = (OpenVPNConnection *) this;
if (oc->events)
RELEASE(oc->events);
oc->events = NULL;
return S_OK;
}
/*
* LogonUI calls this function when our tile is selected
* We do nothing here and let the user choose which profile to connect.
*/
static HRESULT WINAPI
SetSelected(ICCPC *this, BOOL *auto_logon)
{
#ifdef DEBUG
OpenVPNConnection *oc = (OpenVPNConnection *) this;
dmsg(L"profile: %ls", oc->display_name);
#else
(void) this;
#endif
/* setting true here will autoconnect the first entry and Windows will
* autoselect it for the rest of the login session, blocking all other
* entries and other providers, if any. Some commercial products do
* that but its not a good idea, IMO. So we set false here.
*/
*auto_logon = FALSE;
return S_OK;
}
static HRESULT WINAPI
SetDeselected(ICCPC *this)
{
#ifdef DEBUG
OpenVPNConnection *oc = (OpenVPNConnection *) this;
dmsg(L"profile: %ls", oc->display_name);
#else
(void) this;
#endif
return S_OK;
}
/*
* Return display states for a particular field of a tile
*/
static HRESULT WINAPI
GetFieldState(UNUSED ICCPC *this, DWORD field,
CREDENTIAL_PROVIDER_FIELD_STATE *fs,
CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE *fis)
{
HRESULT hr;
dmsg(L"field = %lu", field);
if (field < _countof(field_states))
{
if (fs)
*fs = field_states[field];
if (fis)
*fis = (field_desc[field].cpft == CPFT_SUBMIT_BUTTON) ? CPFIS_FOCUSED : CPFIS_NONE;
hr = S_OK;
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
/*
* Return the string value of the field at the index
*/
static HRESULT WINAPI
GetStringValue(ICCPC *this, DWORD index, WCHAR **ws)
{
HRESULT hr = S_OK;
dmsg(L"index = %lu", index);
OpenVPNConnection *oc = (OpenVPNConnection *) this;
const wchar_t *val = L"";
if (index >= _countof(field_desc))
{
return E_INVALIDARG;
}
/* we have custom field strings only for connection name */
if (field_desc[index].cpft == CPFT_LARGE_TEXT)
{
val = oc->display_name; /* connection name */
}
/* do not to use malloc here as the client will free using CoTaskMemFree */
hr = SHStrDupW(val, ws);
if (!SUCCEEDED(hr))
{
hr = E_OUTOFMEMORY;
dmsg(L"OOM while copying field_strings[%lu]", index);
}
else
{
dmsg(L"Returning %ls", *ws);
}
return hr;
}
/*
* return the image for the tile at index field
*/
static HRESULT WINAPI
GetBitmapValue(UNUSED ICCPC *this, DWORD field, HBITMAP *bmp)
{
dmsg(L"field = %lu ", field);
return E_NOTIMPL;
}
/*
* Return the index of the field the button should be placed adjacent to.
*/
static HRESULT WINAPI
GetSubmitButtonValue(UNUSED ICCPC *this, DWORD field, DWORD *adjacent )
{
HRESULT hr;
dmsg(L"field = %lu", field);
/* we have defined field ids in order of appearance */
if (field_desc[field].cpft == CPFT_SUBMIT_BUTTON && field > 0 && adjacent)
{
*adjacent = field - 1;
hr = S_OK;
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
/*
* Set the value of a field which can accept user text input
* This gets called as user types into a field.
*/
static HRESULT WINAPI
SetStringValue(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED const WCHAR *ws )
{
HRESULT hr;
dmsg(L"field = %lu", field);
/* We do not allow setting any fields -- at least not yet */
hr = E_INVALIDARG;
return hr;
}
static HRESULT WINAPI
GetCheckboxValue(UNUSED ICCPC *this, UNUSED DWORD field,
UNUSED BOOL *checked, UNUSED wchar_t **label)
{
dmsg(L"Entry");
return E_NOTIMPL;
}
static HRESULT WINAPI
GetComboBoxValueCount(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED DWORD *items,
UNUSED DWORD *selected_item)
{
dmsg(L"Entry");
return E_NOTIMPL;
}
static HRESULT WINAPI
GetComboBoxValueAt(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED DWORD item,
UNUSED wchar_t **item_value)
{
dmsg(L"Entry");
return E_NOTIMPL;
}
static HRESULT WINAPI
SetCheckboxValue(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED BOOL checked)
{
dmsg(L"Entry");
return E_NOTIMPL;
}
static HRESULT WINAPI
SetComboBoxSelectedValue(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED DWORD selected_item)
{
dmsg(L"Entry");
return E_NOTIMPL;
}
static HRESULT WINAPI
CommandLinkClicked(UNUSED ICCPC *this, UNUSED DWORD field)
{
dmsg(L"Entry");
return E_NOTIMPL;
}
/*
* Collect the username and password into a serialized credential for the usage scenario
* This gets called soon after connect -- result returned here influences the status of
* Connect.
* We do not support SSO, so no credentials are serialized. We just indicate whether
* we got correct credentials for the Connect process or not.
*/
static HRESULT WINAPI
GetSerialization(ICCPC *this, CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *response,
UNUSED CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *cs, wchar_t **text,
CREDENTIAL_PROVIDER_STATUS_ICON *icon)
{
HRESULT hr = S_OK;
OpenVPNConnection *oc = (OpenVPNConnection *) this;
dmsg(L"Entry: profile <%ls>", oc->display_name);
if (oc->connect_cancelled || !ISCONNECTED(oc->c))
{
dmsg(L"connect cancelled or failed");
/* return _NOT_FINISHED response here for the prompt go
* back to the PLAP tiles screen. Unclear the return value
* should be success or not : using S_OK.
*/
*response = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
if (text && icon)
{
if (oc->connect_cancelled)
hr = SHStrDupW(L"Connection cancelled by user", text);
else if (text)
hr = SHStrDupW(L"Connection failed to complete", text);
*icon = CPSI_ERROR;
}
}
else
{
/* Finished but no credentials to pass on to LogonUI */
*response = CPGSR_NO_CREDENTIAL_FINISHED;
/* return CPGSR_RETURN_NO_CREDENTIAL_FINISHED leads to a loop! */
if (text && icon)
{
hr = SHStrDupW(L"Connected..", text);
*icon = CPSI_SUCCESS;
}
}
return hr;
}
static HRESULT WINAPI
ReportResult(UNUSED ICCPC *this, UNUSED NTSTATUS status, UNUSED NTSTATUS substatus,
UNUSED wchar_t **status_text, UNUSED CREDENTIAL_PROVIDER_STATUS_ICON *icon)
{
dmsg(L"Entry");
return E_NOTIMPL;
}
/* Helper to send status string feedback to events object and qc if available */
static void
NotifyEvents(OpenVPNConnection *oc, const wchar_t *status)
{
if (oc->events)
{
oc->events->lpVtbl->SetFieldString(oc->events, (ICredentialProviderCredential*) oc,
STATUS_FIELD_INDEX, status);
}
if (oc->qc)
{
oc->qc->lpVtbl->SetStatusMessage(oc->qc, status);
}
}
static HRESULT WINAPI
Connect(ICCPC *this, IQueryContinueWithStatus *qc)
{
OpenVPNConnection *oc = (OpenVPNConnection *) this;
wchar_t status[256] = L"";
dmsg(L"profile: <%ls>", oc->display_name);
oc->qc = qc;
NotifyEvents(oc, L"");
oc->connect_cancelled = FALSE;
ConnectHelper(oc->c);
Sleep(100);
while (!ISCONNECTED(oc->c) && !ISDISCONNECTED(oc->c))
{
ShowStatusWindow(oc->c, TRUE);
GetConnectionStatusText(oc->c, status, _countof(status));
NotifyEvents(oc, status);
if (qc->lpVtbl->QueryContinue(qc) != S_OK)
{
/* user wants to cancel */
wcsncpy_s(status, _countof(status), L"Current State: Cancelling", _TRUNCATE);
NotifyEvents(oc, status);
oc->connect_cancelled = TRUE;
DisconnectHelper(oc->c);
break;
}
else if (ISONHOLD(oc->c))
{
ConnectHelper(oc->c);
}
Sleep(100);
}
GetConnectionStatusText(oc->c, status, _countof(status));
NotifyEvents(oc, status);
ShowStatusWindow(oc->c, FALSE);
oc->qc = NULL;
dmsg (L"Exit with: <%ls>", ISCONNECTED(oc->c) ? L"success" : L"error/cancel");
return ISCONNECTED(oc->c) ? S_OK : E_FAIL;
}
static HRESULT WINAPI
Disconnect(ICCPC *this)
{
OpenVPNConnection *oc = (OpenVPNConnection *) this;
dmsg (L"profile <%ls>", oc->display_name);
NotifyEvents(oc, L"Disconnecting");
DisconnectHelper(oc->c);
NotifyEvents(oc, L""); /* clear status text */
return S_OK;
}
HRESULT WINAPI
OVPNConnection_Initialize(OpenVPNConnection *this, connection_t *conn, const wchar_t *display_name)
{
HRESULT hr = S_OK;
this->c = conn;
this->display_name = display_name;
dmsg(L"profile <%ls>", display_name);
return hr;
}
/* Copy field descriptor -- caller will free using CoTaskMemFree, alloc using compatible allocator */
HRESULT CopyFieldDescriptor(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *fd_out,
const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *fd_in)
{
HRESULT hr = S_OK;
memcpy(fd_out, fd_in, sizeof(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR));
/* now copy the label string if present */
if (fd_in->pszLabel)
{
hr = SHStrDupW(fd_in->pszLabel, &fd_out->pszLabel);
}
return hr;
}

64
plap/plap_connection.h Normal file
View File

@ -0,0 +1,64 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifndef PLAP_CONNECTION_H
#define PLAP_CONNECTION_H
#include "plap_common.h"
#include "ui_glue.h"
#include <shlwapi.h>
#include <credentialprovider.h>
/**
* Field descriptors for the connection tiles shown to user.
* array of {index, type, name, guid } -- guid unused here.
*/
static const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR field_desc[] =
{
{ 0, CPFT_TILE_IMAGE, (wchar_t *) L"Image", {0}},
{ 1, CPFT_LARGE_TEXT, (wchar_t *) L"VPN Profile", {0}},
{ 2, CPFT_SUBMIT_BUTTON, (wchar_t *) L"Connect", {0}},
{ 3, CPFT_SMALL_TEXT, (wchar_t *) L"Status", {0}},
};
#define STATUS_FIELD_INDEX 3 /* index to the status text in the above array */
/** Field states -- show image and name always, rest when selected */
static const CREDENTIAL_PROVIDER_FIELD_STATE field_states[] =
{
CPFS_DISPLAY_IN_BOTH, /* image */
CPFS_DISPLAY_IN_BOTH, /* profile name */
CPFS_DISPLAY_IN_SELECTED_TILE, /* button */
CPFS_DISPLAY_IN_SELECTED_TILE, /* status text */
};
/** Helper to deep copy field descriptor */
HRESULT CopyFieldDescriptor(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *fd_out,
const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *fd_in);
typedef struct OpenVPNConnection OpenVPNConnection;
HRESULT WINAPI OVPNConnection_Initialize(OpenVPNConnection *,
connection_t *conn, const wchar_t *display_name);
OpenVPNConnection *OpenVPNConnection_new(void);
#endif /* PLAP_CONNECTION_H */

226
plap/plap_dll.c Normal file
View File

@ -0,0 +1,226 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <initguid.h> /* needed to define (not extern) the GUIDs */
#include <shlwapi.h>
#include <credentialprovider.h>
#include "plap_common.h"
#include "plap_dll.h"
static LONG dll_ref_count = 0;
HINSTANCE hinst_global = NULL;
/*
* Dll Entry Point
*/
BOOL WINAPI
DllMain(HINSTANCE hinstDll, DWORD dwReason, UNUSED LPVOID pReserved)
{
dmsg(L"Entry with dwReason = %lu", dwReason);
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
/* as per MSDN, this reduces the size of application */
DisableThreadLibraryCalls(hinstDll);
init_debug();
break;
case DLL_PROCESS_DETACH:
uninit_debug();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
hinst_global = hinstDll; /* global */
return TRUE;
}
void dll_addref()
{
InterlockedIncrement(&dll_ref_count);
dmsg(L"ref_count after increment = %lu", dll_ref_count);
}
void dll_release()
{
InterlockedDecrement(&dll_ref_count);
dmsg(L"ref_count after decrement = %lu", dll_ref_count);
}
/* A class factory that can create instances of OpenVPNProvider
* This is returned in the exported function DllGetClassObject
* We use a static instance.
*/
/* ClassFactory methods we have to implement */
static ULONG WINAPI AddRef(IClassFactory *this);
static ULONG WINAPI Release(IClassFactory *this);
static HRESULT WINAPI QueryInterface(IClassFactory *this, REFIID riid, void **ppv);
static HRESULT WINAPI CreateInstance(IClassFactory *this, IUnknown *iunk, REFIID riid, void **ppv);
static HRESULT WINAPI LockServer(IClassFactory *this, BOOL lock);
/* template object for creation */
static IClassFactoryVtbl cf_vtbl = {
.QueryInterface = QueryInterface,
.AddRef = AddRef,
.Release = Release,
.CreateInstance = CreateInstance,
.LockServer = LockServer,
};
static IClassFactory cf = {
.lpVtbl = &cf_vtbl
};
/* Implementation */
static ULONG WINAPI
AddRef(UNUSED IClassFactory *this)
{
dll_addref();
return 1; /* we have just 1 static object */
}
static ULONG WINAPI
Release(UNUSED IClassFactory *this)
{
dll_release();
return 1;
}
static HRESULT WINAPI
QueryInterface(IClassFactory *this, REFIID riid, void **ppv)
{
HRESULT hr;
dmsg (L"Entry");
#ifdef DEBUG
debug_print_guid(riid, L"In CF_Queryinterface with iid = ");
#endif
if (ppv != NULL && riid)
{
if (IsEqualIID(&IID_IClassFactory, riid) || IsEqualIID(&IID_IUnknown, riid))
{
*ppv = this;
ADDREF(this);
hr = S_OK;
}
else
{
*ppv = NULL;
hr = E_NOINTERFACE;
}
}
else
{
hr = E_POINTER;
}
return hr;
}
/* Create an instance of OpenVPNProvider and return it in *ppv */
static HRESULT WINAPI
CreateInstance(UNUSED IClassFactory *this, IUnknown *iunk, REFIID riid, void **ppv)
{
HRESULT hr;
dmsg(L"Entry");
#ifdef DEBUG
debug_print_guid(riid, L"In Provider CreateInstance with iid = ");
#endif
*ppv = NULL;
if (!iunk)
{
hr = OpenVPNProvider_CreateInstance(riid, ppv);
}
else
{
hr = CLASS_E_NOAGGREGATION;
}
return hr;
}
/* Windows calls this to keep the server in memory:
* we just increment dll-ref-count so that the dll will not get unloaded.
*/
static HRESULT WINAPI
LockServer(UNUSED IClassFactory *this, BOOL lock)
{
if (lock)
{
dll_addref();
}
else
{
dll_release();
}
return S_OK;
}
/* exported methods */
STDAPI DllCanUnloadNow()
{
HRESULT hr;
dmsg(L"ref_count = %lu", dll_ref_count);
if (dll_ref_count > 0)
{
hr = S_FALSE;
}
else
{
hr = S_OK;
}
return hr;
}
/* Windows calls this on loading the dll and expects a
* ClassFactory instance in *ppv which can be used to
* create class of type rclsid. We support only OpenVPNProvider
* class and check it here.
*/
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
dmsg(L"Entry");
HRESULT hr;
*ppv = NULL;
if (IsEqualIID(&CLSID_OpenVPNProvider, rclsid))
{
ADDREF(&cf); /* though we are reusing a static instance, follow the usual COM semantics */
hr = QUERY_INTERFACE((IClassFactory *) &cf, riid, ppv);
RELEASE((IClassFactory *) &cf);
}
else
{
hr = CLASS_E_CLASSNOTAVAILABLE;
}
return hr;
}

45
plap/plap_dll.h Normal file
View File

@ -0,0 +1,45 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifndef PLAP_DLL_H
#define PLAP_DLL_H
#include <windows.h>
#ifdef __cplusplus
extern "C"
{
#endif
extern HINSTANCE hinst_global;
HRESULT OpenVPNProvider_CreateInstance(REFIID riid, void **ppv);
void dll_addref();
void dll_release();
STDAPI DllCanUnloadNow();
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv);
#ifdef __cplusplus
}
#endif
#endif /* PLAP_DLL_H */

443
plap/plap_provider.c Normal file
View File

@ -0,0 +1,443 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "plap_common.h"
#include "plap_connection.h"
#include "plap_dll.h"
#define MAX_PROFILES 100 /* a large enough number */
/*
* OpenVPNProvider for PLAP: a "class" derived from the base "class"
* ICredentialProvider. In C this is the interface vtable followed
* by our derived class members.
*/
typedef struct OpenVPNProvider
{
const ICredentialProviderVtbl *lpVtbl; /* base interface vtable */
BOOL ui_initialized;
ULONG conn_count;
OpenVPNConnection *connections[MAX_PROFILES];
LONG ref_count;
} OpenVPNProvider;
/* methods we have to implement */
static HRESULT WINAPI QueryInterface(ICredentialProvider *this, REFIID riid, void **ppv);
static ULONG WINAPI AddRef(ICredentialProvider *this);
static ULONG WINAPI Release(ICredentialProvider *this);
static HRESULT WINAPI SetUsageScenario(ICredentialProvider *this,
CREDENTIAL_PROVIDER_USAGE_SCENARIO us, DWORD flags);
static HRESULT WINAPI SetSerialization(ICredentialProvider *this,
const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *cs);
static HRESULT WINAPI Advise(ICredentialProvider *this, ICredentialProviderEvents *e, UINT_PTR context);
static HRESULT WINAPI UnAdvise(ICredentialProvider *this);
static HRESULT WINAPI GetFieldDescriptorCount(ICredentialProvider *this, DWORD *count);
static HRESULT WINAPI GetFieldDescriptorAt(ICredentialProvider *this, DWORD index,
CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR **fd);
static HRESULT WINAPI GetCredentialCount(ICredentialProvider *this, DWORD *count,
DWORD *default_cred, BOOL *autologon_default);
static HRESULT WINAPI GetCredentialAt(ICredentialProvider *this, DWORD index,
ICredentialProviderCredential **c);
/* a helper function for generating our connection array */
static HRESULT CreateOVPNConnectionArray(OpenVPNProvider *op);
/* make a static object for function table */
#define M_(x) .x = x /* I hate typing */
static const ICredentialProviderVtbl icp_vtbl = {
M_(QueryInterface),
M_(AddRef),
M_(Release),
M_(SetUsageScenario),
M_(SetSerialization),
M_(Advise),
M_(UnAdvise),
M_(GetFieldDescriptorCount),
M_(GetFieldDescriptorAt),
M_(GetCredentialCount),
M_(GetCredentialAt)
};
#define ICCPC IConnectableCredentialProviderCredential /* save some more typing */
/* constructor and destructor */
static OpenVPNProvider *
OpenVPNProvider_new(void)
{
dmsg(L"Entry");
OpenVPNProvider *this = calloc(sizeof(*this), 1);
if (this)
{
this->lpVtbl = &icp_vtbl;
this->ref_count = 1; /* we free ourselves when this goes to zero */
dll_addref();
}
return this;
}
static void
OpenVPNProvider_free(OpenVPNProvider *this)
{
dmsg(L"Entry");
for (size_t i = 0; i < this->conn_count; ++i)
{
if (this->connections[i])
{
RELEASE((ICCPC*) this->connections[i]);
}
}
/* Destroy GUI threads and any associated data */
DeleteUI();
free(this);
dll_release();
}
/* Standard methods in every COM object inherited from IUnknown */
static ULONG WINAPI
AddRef(ICredentialProvider *this)
{
OpenVPNProvider *op = (OpenVPNProvider *) this;
dmsg(L"ref_count after addref = %d", op->ref_count+1);
return InterlockedIncrement(&op->ref_count);
}
static ULONG WINAPI
Release(ICredentialProvider *this)
{
OpenVPNProvider *op = (OpenVPNProvider *) this;
ULONG count = InterlockedDecrement(&op->ref_count);
dmsg(L"ref_count after release = %d", op->ref_count);
if (op->ref_count == 0)
{
OpenVPNProvider_free(op); /* suicide -- equivalent of "delete this" */
}
return count;
}
/* In QueryInterface, return *ppv = pointer to the requested interface (riid),
* if we implement the interface.
* In our case we expect riid == IID_ICredentialProvider or IID_IUnknown
*/
static HRESULT WINAPI
QueryInterface(ICredentialProvider *this, REFIID riid, void **ppv)
{
#ifdef DEBUG
debug_print_guid(riid, L"In Provider Queryinterface with iid = ");
#endif
if (!ppv)
{
dmsg(L"ppv is NULL!");
return E_POINTER;
}
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_ICredentialProvider))
{
*ppv = this;
ADDREF(this);
return S_OK;
}
else
{
dmsg(L"unknown iid ignored");
*ppv = NULL;
return E_NOINTERFACE;
}
}
/*
* SetUsageScenario returns success for supported usages -- we support
* only PLAP.
*
* LogonUI calls this while initializing the provider, so we initialize
* our internal data structs, GUI related data, enumerate profiles,
* make a list of connections etc. This is done by calling
* CreateOVPNConnectionArray().
* After this, we should be ready to service calls for connection count
* and individual connection objects.
*/
static HRESULT WINAPI
SetUsageScenario(ICredentialProvider *this,
CREDENTIAL_PROVIDER_USAGE_SCENARIO us, UNUSED DWORD flags)
{
/* I think flags may be ignored for PLAP */
dmsg(L"cpus = %lu", us);
OpenVPNProvider *op = (OpenVPNProvider *) this;
if (us == CPUS_PLAP)
{
return CreateOVPNConnectionArray(op);
}
else
{
return E_NOTIMPL;
}
}
/*
* We do not support SetSerialization, nor expect it
*/
static HRESULT WINAPI
SetSerialization(UNUSED ICredentialProvider *this,
UNUSED const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *cs)
{
dmsg(L"Entry");
return E_NOTIMPL;
}
/*
* called by LogonUI to pass in events ptr -- we ignore this
*/
static HRESULT WINAPI
Advise(UNUSED ICredentialProvider *this,
UNUSED ICredentialProviderEvents *e, UNUSED UINT_PTR ctx)
{
dmsg(L"Entry");
return S_OK;
}
/*
* Called by logonUI when the events callback is no longer valid.
*/
static HRESULT WINAPI
UnAdvise(UNUSED ICredentialProvider *this)
{
dmsg(L"Entry");
return S_OK;
}
/*
* Return the count of descriptors for each connection tile.
* These descriptors are used by LogonUI to display the tile to the user.
* We have a fixed static set for all tiles -- field_desc[] defined
* in the header for OpenVPNConnection.
*/
static HRESULT WINAPI
GetFieldDescriptorCount(UNUSED ICredentialProvider *this, DWORD *count)
{
dmsg(L"Entry");
*count = _countof(field_desc);
return S_OK;
}
/*
* Return the field descriptor for a particular field.
* We have a fixed set of fields to show, but have to return
* an allocated copy in *fd. Must use CoTaskMemAlloc and related
* methods as the caller will use CoTaskMemFree to release memory.
*/
static HRESULT WINAPI
GetFieldDescriptorAt(UNUSED ICredentialProvider *this, DWORD index,
CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR **fd)
{
HRESULT hr = E_OUTOFMEMORY;
dmsg(L"index = %lu", index);
if (index < _countof(field_desc) && fd)
{
/* LogonUI frees this using CoTaskMemFree, so we should not use malloc */
CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *tmp =
(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *)
CoTaskMemAlloc(sizeof(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR));
if (tmp)
{
/* call our copy helper for deep copy */
hr = CopyFieldDescriptor(tmp, &field_desc[index]);
if (SUCCEEDED(hr))
{
*fd = tmp;
}
else
{
CoTaskMemFree(tmp);
}
}
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
/*
* Return the number of available connections.
* default_cred is the one that will be zoomed-in by default.
* As per MSDN, autologon_default causes immediate call to GetSerialization
* for the default item. We don't want this, so set no default.
*/
static HRESULT WINAPI
GetCredentialCount(ICredentialProvider *this, DWORD *count, DWORD *default_cred,
BOOL *autologon_default)
{
OpenVPNProvider *op = (OpenVPNProvider *) this;
*count = op->conn_count;
*default_cred = CREDENTIAL_PROVIDER_NO_DEFAULT;
*autologon_default = FALSE;
dmsg(L"Returning count = %lu, no default", *count);
return S_OK;
}
/*
* Returns the credential at index.
*/
static HRESULT WINAPI
GetCredentialAt(ICredentialProvider *this, DWORD index, ICredentialProviderCredential **ic)
{
HRESULT hr;
dmsg(L"index = %lu", index);
OpenVPNProvider *op = (OpenVPNProvider *) this;
if(index < op->conn_count && ic)
{
hr = QUERY_INTERFACE((ICredentialProviderCredential *) op->connections[index],
&IID_ICredentialProviderCredential, (void **)ic);
/* In our case the same as *ic = op->connections[index], but the above is standard COM way
* which checks the IID and increments ref-count as well */
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
/*
* Create connection objects from available config files
*/
static HRESULT
CreateOVPNConnectionArray(OpenVPNProvider *op)
{
HRESULT hr = S_OK;
dmsg(L"Entry");
if (op->ui_initialized) /* Already initialized */
{
return hr;
}
/* delete previous connections if any */
for (size_t i = 0; i < op->conn_count; i++)
{
RELEASE((ICCPC*) op->connections[i]);
}
op->conn_count = 0;
if (InitializeUI(hinst_global) != 0) /* init GUI data structs */
{
return E_FAIL;
}
op->ui_initialized = 1;
dmsg(L"UI initialized");
/* Create a connection object for every config that could be prestarted */
connection_t *c[MAX_PROFILES];
DWORD count = FindPLAPConnections(c, _countof(c));
for (DWORD i = 0; i < count; i++)
{
OpenVPNConnection *oc = OpenVPNConnection_new();
if (oc)
{
/* we have to initialize OpenVPNConnection objects before use */
hr = OVPNConnection_Initialize(oc, c[i], ConfigDisplayName(c[i]));
if (SUCCEEDED(hr))
{
op->connections[op->conn_count++] = oc;
}
else
{
RELEASE((ICCPC*) oc);
}
}
else
{
hr = E_OUTOFMEMORY;
break;
}
dmsg(L"added connection object for <%ls>", ConfigDisplayName(c[i]));
}
return hr;
}
/* A helper function for use by DllGetClassObject in plap_dll.c
* This is the only non-static function in this file.
*/
HRESULT
OpenVPNProvider_CreateInstance(REFIID riid, void **ppv)
{
HRESULT hr;
#ifdef DEBUG
debug_print_guid(riid, L"In Provider CreateInstance with iid = ");
#endif
OpenVPNProvider *p = OpenVPNProvider_new();
if (p)
{
hr = QUERY_INTERFACE((ICredentialProvider *) p, riid, ppv);
RELEASE((ICredentialProvider *) p);
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}

192
plap/stub.c Normal file
View File

@ -0,0 +1,192 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "plap_common.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include "options.h"
#include "main.h"
#include "localization.h"
#include "ui_glue.h"
void PrintDebugMsg(wchar_t *msg)
{
x_dmsg("GUI-source", "", msg);
}
void
ErrorExit(UNUSED int exit_code, const wchar_t *msg)
{
if (msg)
MessageBoxExW(NULL, msg, TEXT(PACKAGE_NAME),
MB_OK | MB_SETFOREGROUND|MB_ICONERROR, GetGUILanguage());
DetachAllOpenVPN();
/* do not exit */
}
void RecreatePopupMenus(void)
{
return;
}
void CreatePopupMenus(void)
{
return;
}
void OnNotifyTray(UNUSED LPARAM lp)
{
return;
}
void OnDestroyTray(void)
{
return;
}
void ShowTrayIcon(void)
{
return;
}
void SetTrayIcon(UNUSED conn_state_t state)
{
return;
}
void SetMenuStatus(UNUSED connection_t *c, UNUSED conn_state_t state)
{
return;
}
void SetMenuStatusById(UNUSED int id, UNUSED conn_state_t state)
{
return;
}
void SetServiceMenuStatus(void)
{
return;
}
void ShowTrayBalloon(UNUSED wchar_t *s1, UNUSED wchar_t *s2)
{
return;
}
void CheckAndSetTrayIcon(void)
{
return;
}
void RunPreconnectScript(UNUSED connection_t *c)
{
return;
}
void RunConnectScript(UNUSED connection_t *ic, UNUSED int run_as_service)
{
return;
}
void RunDisconnectScript(UNUSED connection_t *c, UNUSED int run_as_service)
{
return;
}
int SaveKeyPass(UNUSED const WCHAR *config_name, UNUSED const WCHAR *password)
{
return 1;
}
int SaveAuthPass(UNUSED const WCHAR *config_name, UNUSED const WCHAR *password)
{
return 1;
}
int SaveUsername(UNUSED const WCHAR *config_name, UNUSED const WCHAR *username)
{
return 1;
}
int RecallKeyPass(UNUSED const WCHAR *config_name, UNUSED WCHAR *password)
{
return 0;
}
int RecallAuthPass(UNUSED const WCHAR *config_name, UNUSED WCHAR *password)
{
return 0;
}
int RecallUsername(UNUSED const WCHAR *config_name, UNUSED WCHAR *username)
{
return 0;
}
void DeleteSavedAuthPass(UNUSED const WCHAR *config_name)
{
return;
}
void DeleteSavedKeyPass(UNUSED const WCHAR *config_name)
{
return;
}
void DeleteSavedPasswords(UNUSED const WCHAR *config_name)
{
return;
}
BOOL IsAuthPassSaved(UNUSED const WCHAR *config_name)
{
return 0;
}
BOOL IsKeyPassSaved(UNUSED const WCHAR *config_name)
{
return 0;
}
void env_item_del_all(UNUSED struct env_item *head)
{
return;
}
void process_setenv(UNUSED connection_t *c, UNUSED time_t timestamp, UNUSED const char *msg)
{
return;
}
BOOL AuthorizeConfig(UNUSED const connection_t *c)
{
return 1;
}
void echo_msg_process(UNUSED connection_t *c, UNUSED time_t timestamp, UNUSED const char *msg)
{
return;
}
void echo_msg_clear(UNUSED connection_t *c, UNUSED BOOL clear_history)
{
return;
}
void echo_msg_load(UNUSED connection_t *c)
{
return;
}
BOOL GetRandomPassword(char *p, size_t len)
{
if (p && len > 0) *p = '\0';
return 0;
}
BOOL CheckKeyFileWriteAccess(UNUSED connection_t *c)
{
return 0;
}

358
plap/ui_glue.c Normal file
View File

@ -0,0 +1,358 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#if !defined (UNICODE)
#error UNICODE and _UNICODE must be defined. This version only supports unicode builds.
#endif
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "plap_common.h"
#include "ui_glue.h"
#include "main.h"
#include "options.h"
#include "openvpn.h"
#include "manage.h"
#include "openvpn_config.h"
#include "proxy.h"
#include "registry.h"
#include "openvpn-gui-res.h"
#include "localization.h"
#include "misc.h"
/* Global options structure */
options_t o;
int state_connected = connected, state_disconnected = disconnected,
state_onhold = onhold;
/* Initialize GUI data structures. Returns 0 on success */
DWORD
InitializeUI(HINSTANCE hinstance)
{
WSADATA wsaData;
/* a session local semaphore to detect second instance */
HANDLE session_semaphore = InitSemaphore(L"Local\\"PACKAGE_NAME"-PLAP");
srand(time(NULL));
/* try to lock the semaphore, else we are not the first instance */
if (session_semaphore
&& WaitForSingleObject(session_semaphore, 200) != WAIT_OBJECT_0)
{
if (hinstance == o.hInstance)
{
/* already initialized */
return 0;
}
else
{
MsgToEventLog(EVENTLOG_ERROR_TYPE, L"InitializeUI called a second time with a different hinstance -- multiple instances of the UI not supported.");
return 1;
}
}
dmsg(L"Starting OpenVPN UI v%hs", PACKAGE_VERSION);
if(!GetModuleHandle(_T("RICHED20.DLL")))
{
LoadLibrary(_T("RICHED20.DLL"));
}
else
{
MsgToEventLog(EVENTLOG_ERROR_TYPE, LoadLocalizedString(IDS_ERR_LOAD_RICHED20));
return 1;
}
/* Initialize handlers for management interface notifications
* Some handlers are replaced by local functions
*/
mgmt_rtmsg_handler handler[] = {
{ ready_, OnReady },
{ hold_, OnHold },
{ log_, OnLogLine },
{ state_, OnStateChange },
{ password_, OnPassword },
{ proxy_, OnProxy },
{ stop_, OnStop },
{ needok_, OnNeedOk },
{ needstr_, OnNeedStr },
{ echo_, OnEcho },
{ bytecount_,OnByteCount },
{ infomsg_, OnInfoMsg },
{ timeout_, OnTimeout },
{ 0, NULL}
};
InitManagement(handler);
dmsg(L"Init Management done");
/* initialize options to default state */
InitOptions(&o);
o.session_semaphore = session_semaphore;
dmsg(L"InitOptions done");
GetRegistryKeys();
dmsg(L"GetRegistryKeys done");
/* Do not show status window by default */
o.silent_connection = 1;
o.disable_save_passwords = 1;
o.disable_popup_messages = 1;
o.enable_persistent = 1;
/* In case we get queried for proxy, we currently support only system proxy */
o.proxy_source = windows;
/* Force scanning persistent connections -- we still need the service
* running, but in case the service start-up is delayed, this helps
* as we scan for profiles only once during a login session.
* We expect users who register the PLAP dll to also enable the service.
*/
o.service_state = service_connected;
o.hInstance = hinstance;
DWORD status = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (status)
{
MsgToEventLog(EVENTLOG_ERROR_TYPE, L"WSAStartup Failed with error = %lu", status);
return status;
}
dmsg(L"WSAStartup Done");
BuildFileList();
dmsg(L"BuildFileList Done");
dpi_initialize(&o);
return 0;
}
/* Returns number of PLAP enabled configs -- for now
* same as autostarted (persistent) connections.
* The corresponding connection pointers are set in
* the conn[] array.
*/
DWORD
FindPLAPConnections(connection_t *conn[], size_t max_count)
{
DWORD count = 0;
for (int i = 0; i < o.num_configs && count < max_count; i++)
{
connection_t *c = &o.conn[i];
if (!(c->flags & FLAG_DAEMON_PERSISTENT))
{
continue;
}
conn[count++] = c;
}
return count;
}
static void
WaitOnThread (connection_t *c, DWORD timeout)
{
HANDLE h = OpenThread(THREAD_ALL_ACCESS, FALSE, c->threadId);
if (!h)
{
dmsg(L"Failed to get handle to the connection thread");
goto out;
}
DWORD exit_code;
if (WaitForSingleObject(h, timeout) == WAIT_OBJECT_0 &&
GetExitCodeThread(h, &exit_code) && exit_code != STILL_ACTIVE)
{
dmsg(L"Connection thread closed");
goto out;
}
/* Kill the thread */
dmsg(L"Force terminating a connection thread");
TerminateThread (h, 1);
c->hwndStatus = NULL;
c->threadId = 0;
out:
if (h && h != INVALID_HANDLE_VALUE)
{
CloseHandle(h);
}
return;
}
void
GetConnectionStatusText(connection_t *c, wchar_t *status, DWORD len)
{
wchar_t status_text[256] = L"";
/* blank status as a default */
if (len > 0)
{
*status = '\0';
}
if (c->hwndStatus)
{
GetDlgItemText(c->hwndStatus, ID_TXT_STATUS, status_text, _countof(status_text));
/* showing RECONNECTING while on hold is confusing, use status text */
if ((strcmp(c->daemon_state, "RECONNECTING") == 0) && c->state == onhold && *status_text)
{
wcsncpy_s(status, len, status_text, _TRUNCATE);
}
else if (*c->daemon_state) /* this is more fine-grained and thus preferred */
{
__sntprintf_0(status, len, L"%hs", c->daemon_state);
}
else if (*status_text)
{
wcsncpy_s(status, len, status_text, _TRUNCATE);
}
}
}
void
SetParentWindow(HWND hwnd)
{
o.hWnd = hwnd;
}
void
ShowStatusWindow(connection_t *c, BOOL show)
{
if (c->hwndStatus)
{
/* Do not enable detach button in the PLAP mode */
ShowWindowAsync(GetDlgItem(c->hwndStatus, ID_DETACH), SW_HIDE);
/* Disconnecting from status Window gives no feedback to progress dialog.
* Do not show the disconnect button.
*/
ShowWindowAsync(GetDlgItem(c->hwndStatus, ID_DISCONNECT), SW_HIDE);
ShowWindowAsync(c->hwndStatus, show ? SW_SHOW : SW_HIDE);
}
}
void
DetachAllOpenVPN()
{
int i;
/* Detach from the mgmt i/f of all connections */
for (i = 0; i < o.num_configs; i++)
{
if (o.conn[i].state != disconnected)
{
DetachOpenVPN(&o.conn[i]);
}
}
/* Wait for all connections to detach (Max 1 sec) */
for (i = 0; i < 10; i++)
{
if (CountConnState(disconnected) == o.num_configs)
{
break;
}
Sleep(100);
}
for (i = 0; i < o.num_configs; i++)
{
if (o.conn[i].hwndStatus)
{
/* Status thread still running? kill it */
WaitOnThread(&o.conn[i], 0);
}
}
}
const wchar_t *
ConfigDisplayName(connection_t *c)
{
return c->config_name;
}
int
ConnectionState(connection_t *c)
{
return c->state;
}
void
DeleteUI(void)
{
if (!o.hInstance) /* not initialized? */
{
dmsg(L"DeleteUI called before InitializeUI");
}
DetachAllOpenVPN();
CloseSemaphore(o.session_semaphore);
WSACleanup();
memset (&o, 0, sizeof(o));
}
void
ConnectHelper(connection_t *c)
{
if (c->state == disconnected || c->state == detached)
{
StartOpenVPN(c);
dmsg(L"Calling StartOpenVPN on <%ls>", c->config_name);
}
else if (c->state == onhold)
{
ReleaseOpenVPN(c);
dmsg(L"Calling ReleaseOpenVPN on <%ls>", c->config_name);
}
}
void
DisconnectHelper(connection_t *c)
{
if (c->state == disconnected || c->state == onhold
|| c->manage.connected < 2) /* mgmt not yet ready for input */
{
return;
}
/* disconnect will not work if disconnect button is disabled on the status
* window -- enable it until out of here.
*/
ShowWindowAsync(GetDlgItem(c->hwndStatus, ID_DISCONNECT), SW_SHOW);
dmsg(L"sending stop");
StopOpenVPN(c);
/* wait up to a few sec for state to change */
time_t timeout = time(NULL) + 5;
while (timeout > time(NULL) && c->state != onhold && c->state != disconnected)
{
Sleep(100);
}
ShowWindowAsync(GetDlgItem(c->hwndStatus, ID_DISCONNECT), SW_HIDE);
dmsg(L"profile: %ls state = %d", c->config_name, c->state);
}

108
plap/ui_glue.h Normal file
View File

@ -0,0 +1,108 @@
/*
* OpenVPN-PLAP-Provider
*
* Copyright (C) 2017-2022 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 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
*/
#ifndef UI_LINK_H
#define UI_LINK_H
#ifdef __cplusplus
extern "C" {
#endif
#include <winsock2.h> /* suppress warning about order of includes */
#include <commctrl.h>
typedef struct connection connection_t;
/* access to GUI states without exposing the options header */
extern int state_connected, state_disconnected, state_onhold;
/**
* Initialize GUI data structures
* @param hinstance of the calling process/dll context
* @return 0 on success > 0 on error
*/
DWORD InitializeUI(HINSTANCE hinstance);
/**
* Close threads, cleanup resources created by Initialize
*/
void DeleteUI(void);
/**
* Enumerate PLAP enabled connection profiles.
*
* @param conn[] On output this contains an array of connection_t
* structs for PLAP enabled profiles
* @param max_count size of conn[] array on input
* @returns the number of connection profiles found.
*/
DWORD FindPLAPConnections(connection_t *conn[], size_t max_count);
/**
* Display name of a connection profile.
*
* @param c connection_t struct for the profile (input)
* @returns a constant wide char string
*/
const wchar_t *ConfigDisplayName(connection_t *c);
/**
* State of a connection
*/
int ConnectionState(connection_t *c);
/**
* Textual description of current connection state
*
* @param status On input must have space of at least len wide characters
* On output contains the NUL-terminated status text
* @param len Capacity of memory pointed to by status
*/
void GetConnectionStatusText(connection_t *c, WCHAR *status, DWORD len);
/**
* Set hwnd as the parent window handle for dialogs
*/
void SetParentWindow(HWND hwnd);
void DetachAllOpenVPN();
/**
* Enable/Disable display of status Window for connection c
*/
void ShowStatusWindow(connection_t *c, BOOL show);
/**
* Start or Release OpenVPN connection for c
* The connection is completed asynchronously.
*/
void ConnectHelper(connection_t *c);
/**
* Stop OpenVPN connection for c
* After initiating a disconnection, this polls for the connection status
* and waits for the disconnection to complete for a max of 5 seconds.
*/
void DisconnectHelper(connection_t *c);
#ifdef __cplusplus
}
#endif
#endif /* UI_LINK_H */