Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
5d5e14b724 | |||
1d583c39c9 | |||
9926cd19ee | |||
daea1bba62 | |||
f05f8dab76 | |||
ba11abff1d | |||
22f578b451 | |||
7bc76ac54c | |||
5e35c5769a | |||
3f888dd781 | |||
5b065bce3d | |||
d8a1fe95aa | |||
f57ee6c0f8 | |||
3364719ba1 | |||
e46739a510 | |||
adc860fa6e | |||
d4cc3053a6 | |||
38c6189d5f | |||
bf4c4d846e | |||
5040ebba8e | |||
4598528765 | |||
8fbcf27f6a | |||
48d46617d2 | |||
e7d5ecb50b | |||
6a77f3f7b3 | |||
a4673f9fb9 | |||
c84a8b5a70 | |||
31a3b67cba |
Binary file not shown.
Binary file not shown.
21
README.md
21
README.md
@@ -1,8 +1,10 @@
|
||||
# GÉANTLink
|
||||
|
||||
Suite of EAP supplicants for Microsoft Windows - IEEE 802.1X plug-ins for enterprise network authentication
|
||||
Suite of EAP supplicants for Microsoft Windows - IEEE 802.1X clients for enterprise network authentication
|
||||
|
||||
## Features
|
||||
- Integrates into Windows seamlessly
|
||||
- Wired and wireless network support
|
||||
|
||||
### Authentication methods
|
||||
- EAP-TTLS with the following inner methods:
|
||||
@@ -14,7 +16,7 @@ Suite of EAP supplicants for Microsoft Windows - IEEE 802.1X plug-ins for enterp
|
||||
|
||||
### Security
|
||||
- Microsoft Windows Credential Manager stored user credentials
|
||||
- User credentials can be shared between different network profiles
|
||||
- User credentials can be shared between different network profiles, regardless of their connection: wired or wireless
|
||||
- Encrypted EapHost inter-process communication
|
||||
- TLS:
|
||||
- Separate trusted root CA list
|
||||
@@ -30,11 +32,21 @@ Suite of EAP supplicants for Microsoft Windows - IEEE 802.1X plug-ins for enterp
|
||||
- Lockable network profile configuration
|
||||
|
||||
### Deployment
|
||||
- Released as multi-lingual 32 and 64-bit MSI packages
|
||||
- Released as multi-lingual 32 and 64-bit MSI packages; Group Policy deployment supported
|
||||
- [MsiUseFeature utility](https://github.com/Amebis/GEANTLink/tree/master/MsiUseFeature) for GÉANTLink install state testing (for embedding GÉANTLink into other setup packages)
|
||||
- [CredWrite utility](https://github.com/Amebis/GEANTLink/tree/master/CredWrite) for automated user credential import to Credential Manager
|
||||
- [WLANManager utility](https://github.com/Amebis/GEANTLink/tree/master/WLANManager) to allow network profile configuration dialog shortcuts
|
||||
|
||||
### Supported operating systems
|
||||
- Windows Vista, Windows Server 2008
|
||||
- Windows 7, Windows Server 2008 R2
|
||||
- Windows 8 Desktop, Windows Server 2012
|
||||
- Windows 8.1 Desktop, Windows Server 2012 R2
|
||||
- Windows 10 Desktop, Windows Server 2016
|
||||
|
||||
## Download
|
||||
Binaries are available for download [here](https://github.com/Amebis/GEANTLink/releases).
|
||||
|
||||
## Building
|
||||
|
||||
### Building Environment Requirements
|
||||
@@ -117,3 +129,6 @@ Command | Explanation
|
||||
`nmake SetupDebug` | Builds a debug version of project and debug MSI setup files. The resulting files can be found in `output\Setup` folder.
|
||||
|
||||
The `/ls` flag can be appended to the commands above to reduce NMAKE’s verbosity. You can combine multiple targets (i.e. nmake Unregister Clean). Please, see NMAKE reference for further reading.
|
||||
|
||||
### Translating into your language
|
||||
GÉANTLink is fully localizable. We kindly invite you to help [translating it on Transifex](https://www.transifex.com/eduroam_devel/geantlink/).
|
||||
|
@@ -60,6 +60,10 @@ static int WLANManager()
|
||||
return -1;
|
||||
}
|
||||
|
||||
wstring interface_name;
|
||||
if (nArgs >= 5 && _wcsicmp(pwcArglist[3], L"interface") == 0)
|
||||
interface_name = pwcArglist[4];
|
||||
|
||||
// Open WLAN handle.
|
||||
DWORD dwNegotiatedVersion;
|
||||
wlan_handle wlan;
|
||||
@@ -83,21 +87,55 @@ static int WLANManager()
|
||||
interfaces.reset(pInterfaceList);
|
||||
}
|
||||
|
||||
tstring_guid devclass_net(GUID_DEVCLASS_NET);
|
||||
bool profile_found = false;
|
||||
for (DWORD i = 0; i < interfaces->dwNumberOfItems; i++) {
|
||||
if (interfaces->InterfaceInfo[i].isState == wlan_interface_state_not_ready) {
|
||||
// This interface is not ready.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!interface_name.empty()) {
|
||||
// Read the interface name from registry.
|
||||
reg_key key;
|
||||
if (key.open(HKEY_LOCAL_MACHINE, tstring_printf(_T("SYSTEM\\CurrentControlSet\\Control\\Network\\%s\\%s\\Connection"), devclass_net.c_str(), tstring_guid(interfaces->InterfaceInfo[i].InterfaceGuid).c_str()).c_str(), 0, KEY_READ)) {
|
||||
wstring name;
|
||||
if (RegQueryStringValue(key, _T("Name"), name) == ERROR_SUCCESS && _wcsicmp(interface_name.c_str(), name.c_str()) != 0) {
|
||||
// Not the interface we are interested in.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<WLAN_PROFILE_INFO_LIST, WlanFreeMemory_delete<WLAN_PROFILE_INFO_LIST> > profiles;
|
||||
{
|
||||
// Get a list of profiles.
|
||||
WLAN_PROFILE_INFO_LIST *pProfileList;
|
||||
DWORD dwResult = WlanGetProfileList(wlan, &(interfaces->InterfaceInfo[i].InterfaceGuid), NULL, &pProfileList);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
DisplayError(_T("%s function failed (error %u)."), _T("WlanGetProfileList"), dwResult);
|
||||
return 4;
|
||||
}
|
||||
profiles.reset(pProfileList);
|
||||
}
|
||||
|
||||
for (DWORD j = 0; j < profiles->dwNumberOfItems; j++)
|
||||
if (_wcsicmp(profiles->ProfileInfo[j].strProfileName, pwcArglist[2]) == 0) {
|
||||
profile_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!profile_found)
|
||||
continue;
|
||||
|
||||
// Launch WLAN profile config dialog.
|
||||
// Note: When a debugger is attached to this process the WlanUIEditProfile() will raise an exception and fail.
|
||||
WLAN_REASON_CODE wlrc;
|
||||
WLAN_REASON_CODE wlrc = L2_REASON_CODE_SUCCESS;
|
||||
DWORD dwResult = WlanUIEditProfile(WLAN_UI_API_VERSION, pwcArglist[2], &(interfaces->InterfaceInfo[i].InterfaceGuid), NULL, WLSecurityPage, NULL, &wlrc);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
DisplayError(_T("%s function failed (error %u)."), _T("WlanUIEditProfile"), dwResult);
|
||||
// WlanUIEditProfile() displays own error dialog on failure.
|
||||
//DisplayError(_T("%s function failed (error %u)."), _T("WlanUIEditProfile"), dwResult);
|
||||
return 5;
|
||||
}
|
||||
if (wlrc != WLAN_REASON_CODE_SUCCESS) {
|
||||
} else if (wlrc != WLAN_REASON_CODE_SUCCESS) {
|
||||
tstring reason;
|
||||
if (WlanReasonCodeToString(wlrc, reason, NULL) == ERROR_SUCCESS)
|
||||
DisplayError(_T("%s function failed: %s"), _T("WlanUIEditProfile"), reason.c_str());
|
||||
@@ -108,6 +146,9 @@ static int WLANManager()
|
||||
break;
|
||||
}
|
||||
|
||||
if (!profile_found)
|
||||
DisplayError(_T("%ls profile not found."), pwcArglist[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -119,18 +160,26 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In
|
||||
UNREFERENCED_PARAMETER(lpCmdLine);
|
||||
UNREFERENCED_PARAMETER(nCmdShow);
|
||||
|
||||
int res = 0;
|
||||
|
||||
{
|
||||
// Initialize Windows XP visual styles
|
||||
INITCOMMONCONTROLSEX icc;
|
||||
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
||||
icc.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES | ICC_LINK_CLASS;
|
||||
InitCommonControlsEx(&icc);
|
||||
// Note: When a debugger is attached to this process, the WlanUIEditProfile() will raise an exception and fail.
|
||||
// It was accidentially discovered, that COM initialization resolves this issue.
|
||||
com_initializer com_init(NULL);
|
||||
|
||||
{
|
||||
// Initialize Windows XP visual styles
|
||||
INITCOMMONCONTROLSEX icc;
|
||||
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
||||
icc.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES | ICC_LINK_CLASS;
|
||||
InitCommonControlsEx(&icc);
|
||||
}
|
||||
|
||||
pfnWlanReasonCodeToString = WlanReasonCodeToString;
|
||||
|
||||
res = WLANManager();
|
||||
}
|
||||
|
||||
pfnWlanReasonCodeToString = WlanReasonCodeToString;
|
||||
|
||||
int res = WLANManager();
|
||||
|
||||
assert(!_CrtDumpMemoryLeaks());
|
||||
return res;
|
||||
}
|
||||
|
@@ -1,12 +1,13 @@
|
||||
#WLANManager
|
||||
# WLANManager
|
||||
Invokes standard Windows Wireless Network Properties dialog
|
||||
|
||||
##Usage
|
||||
## Usage
|
||||
```
|
||||
WLANManager profile <name>
|
||||
WLANManager profile <profile name> [interface <interface name>]
|
||||
```
|
||||
|
||||
- `name` - The name of the network profile (not neccessarely the same as SSID)
|
||||
- `profile name` - The name of the network profile (not neccessarely the same as SSID)
|
||||
- `interface name` - The name of the specific network interface to search the profile at
|
||||
|
||||
Return codes:
|
||||
- -1 = Invalid parameters
|
||||
|
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <Windows.h>
|
||||
#include <CommCtrl.h>
|
||||
#include <devguid.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include <memory>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2016 Amebis
|
||||
Copyright 2016 GÉANT
|
||||
Copyright 2016 GÉANT
|
||||
|
||||
This file is part of GÉANTLink.
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2016 Amebis
|
||||
Copyright 2016 GÉANT
|
||||
Copyright 2016 GÉANT
|
||||
|
||||
This file is part of GÉANTLink.
|
||||
|
||||
|
@@ -29,7 +29,7 @@
|
||||
// Product version as a single DWORD
|
||||
// Note: Used for version comparison within C/C++ code.
|
||||
//
|
||||
#define PRODUCT_VERSION 0x01020200
|
||||
#define PRODUCT_VERSION 0x01020500
|
||||
|
||||
//
|
||||
// Product version by components
|
||||
@@ -39,26 +39,26 @@
|
||||
//
|
||||
#define PRODUCT_VERSION_MAJ 1
|
||||
#define PRODUCT_VERSION_MIN 2
|
||||
#define PRODUCT_VERSION_REV 2
|
||||
#define PRODUCT_VERSION_REV 5
|
||||
#define PRODUCT_VERSION_BUILD 0
|
||||
|
||||
//
|
||||
// Human readable product version and build year for UI
|
||||
//
|
||||
#define PRODUCT_VERSION_STR "1.2-beta1"
|
||||
#define PRODUCT_BUILD_YEAR_STR "2016"
|
||||
#define PRODUCT_VERSION_STR "1.2a"
|
||||
#define PRODUCT_BUILD_YEAR_STR "2017"
|
||||
|
||||
//
|
||||
// Numerical version presentation for ProductVersion propery in
|
||||
// MSI packages (syntax: N.N[.N[.N]])
|
||||
//
|
||||
#define PRODUCT_VERSION_INST "1.2.2"
|
||||
#define PRODUCT_VERSION_INST "1.2.5"
|
||||
|
||||
//
|
||||
// The product code for ProductCode property in MSI packages
|
||||
// Replace with new on every version change, regardless how minor it is.
|
||||
//
|
||||
#define PRODUCT_VERSION_GUID "{FF5DD25D-917C-4B33-81D3-C75B5E1C8CA8}"
|
||||
#define PRODUCT_VERSION_GUID "{B43F5526-283F-4634-8841-51B7CB6D5AEC}"
|
||||
|
||||
//
|
||||
// Product vendor
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2016 Amebis
|
||||
Copyright 2016 GÉANT
|
||||
Copyright 2016 GÉANT
|
||||
|
||||
This file is part of GÉANTLink.
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2016 Amebis
|
||||
Copyright 2016 GÉANT
|
||||
Copyright 2016 GÉANT
|
||||
|
||||
This file is part of GÉANTLink.
|
||||
|
||||
|
@@ -360,6 +360,14 @@ public:
|
||||
///
|
||||
/// Constructs a credential dialog
|
||||
///
|
||||
/// \param[in] prov Provider configuration data
|
||||
/// \param[in] parent Parent window
|
||||
/// \param[in] id An identifier for the dialog. A value of -1 is taken to mean a default.
|
||||
/// \param[in] title The title of the dialog
|
||||
/// \param[in] pos The dialog position. The value \c wxDefaultPosition indicates a default position, chosen by either the windowing system or wxWidgets, depending on platform.
|
||||
/// \param[in] size The dialog size. The value \c wxDefaultSize indicates a default size, chosen by either the windowing system or wxWidgets, depending on platform.
|
||||
/// \param[in] style The window style
|
||||
///
|
||||
wxEAPCredentialsDialog(const eap::config_provider &prov, wxWindow *parent, wxWindowID id = wxID_ANY, const wxString &title = _("EAP Credentials"), const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE);
|
||||
};
|
||||
|
||||
|
Binary file not shown.
@@ -108,10 +108,10 @@ void eap::config_method_eapgtc::load(_In_ IXMLDOMNode *pConfigRoot)
|
||||
} else
|
||||
throw invalid_argument(string_printf(__FUNCTION__ " Unsupported authentication mode (%ls).", (BSTR)auth_mode));
|
||||
|
||||
m_module.log_config((xpath + L"/AuthMode").c_str(), (BSTR)auth_mode);
|
||||
|
||||
// Load method configuration.
|
||||
config_method_with_cred::load(pConfigRoot);
|
||||
|
||||
m_module.log_config((xpath + L"/AuthMode").c_str(), auth_mode);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -100,6 +100,14 @@ public:
|
||||
///
|
||||
/// Constructs a credential dialog
|
||||
///
|
||||
/// \param[in] prov Provider configuration data
|
||||
/// \param[in] parent Parent window
|
||||
/// \param[in] id An identifier for the dialog. A value of -1 is taken to mean a default.
|
||||
/// \param[in] title The title of the dialog
|
||||
/// \param[in] pos The dialog position. The value \c wxDefaultPosition indicates a default position, chosen by either the windowing system or wxWidgets, depending on platform.
|
||||
/// \param[in] size The dialog size. The value \c wxDefaultSize indicates a default size, chosen by either the windowing system or wxWidgets, depending on platform.
|
||||
/// \param[in] style The window style
|
||||
///
|
||||
wxGTCResponseDialog(const eap::config_provider &prov, wxWindow *parent, wxWindowID id = wxID_ANY, const wxString &title = _("GTC Challenge"), const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE);
|
||||
};
|
||||
|
||||
|
@@ -55,7 +55,6 @@ wxTLSServerTrustPanelBase::wxTLSServerTrustPanelBase( wxWindow* parent, wxWindow
|
||||
sb_root_ca_btn->Add( m_root_ca_add_file, 0, wxRIGHT|wxLEFT, 5 );
|
||||
|
||||
m_root_ca_remove = new wxButton( sb_server_trust->GetStaticBox(), wxID_ANY, _("&Remove CA"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_root_ca_remove->Enable( false );
|
||||
m_root_ca_remove->SetToolTip( _("Removes selected certificate authorities from the list") );
|
||||
|
||||
sb_root_ca_btn->Add( m_root_ca_remove, 0, wxLEFT, 5 );
|
||||
|
@@ -659,7 +659,7 @@
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">0</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
|
@@ -43,7 +43,7 @@ public:
|
||||
protected:
|
||||
static wxCriticalSection s_lock; ///< Initialization lock
|
||||
static unsigned long s_init_ref_count; ///< Initialization reference counter
|
||||
static wxLocale s_locale; ///< Locale
|
||||
static wxLocale *s_locale; ///< Locale
|
||||
};
|
||||
|
||||
|
||||
@@ -118,19 +118,27 @@ void eap::peer_ttls_ui::invoke_config_ui(
|
||||
// Initialize application.
|
||||
wxInitializerPeer init(m_instance);
|
||||
|
||||
{
|
||||
wxWindow *parent;
|
||||
if (hwndParent) {
|
||||
// Create wxWidget-approved parent window.
|
||||
wxWindow parent;
|
||||
parent.SetHWND((WXHWND)(hwndParent ? hwndParent : GetForegroundWindow()));
|
||||
parent.AdoptAttributesFromHWND();
|
||||
wxTopLevelWindows.Append(&parent);
|
||||
parent = new wxWindow;
|
||||
parent->SetHWND((WXHWND)hwndParent);
|
||||
parent->AdoptAttributesFromHWND();
|
||||
wxTopLevelWindows.Append(parent);
|
||||
} else
|
||||
parent = NULL;
|
||||
|
||||
// Create and launch configuration dialog.
|
||||
wxEAPConfigDialog<wxTTLSConfigWindow> dlg(cfg, &parent);
|
||||
result = dlg.ShowModal();
|
||||
// Create and launch configuration dialog.
|
||||
wxEAPConfigDialog<wxTTLSConfigWindow> dlg(cfg, parent);
|
||||
if (!parent) {
|
||||
FLASHWINFO fwi = { sizeof(FLASHWINFO), dlg.GetHWND(), FLASHW_ALL | FLASHW_TIMERNOFG };
|
||||
::FlashWindowEx(&fwi);
|
||||
}
|
||||
result = dlg.ShowModal();
|
||||
|
||||
wxTopLevelWindows.DeleteObject(&parent);
|
||||
parent.SetHWND((WXHWND)NULL);
|
||||
if (parent) {
|
||||
wxTopLevelWindows.DeleteObject(parent);
|
||||
parent->SetHWND((WXHWND)NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +161,9 @@ void eap::peer_ttls_ui::invoke_identity_ui(
|
||||
_Out_ DWORD *pdwUserDataOutSize,
|
||||
_Out_ LPWSTR *ppwszIdentity)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
//::Sleep(10000);
|
||||
#endif
|
||||
assert(ppwszIdentity);
|
||||
|
||||
// Unpack configuration.
|
||||
@@ -178,204 +189,220 @@ void eap::peer_ttls_ui::invoke_identity_ui(
|
||||
// Initialize application.
|
||||
wxInitializerPeer init(m_instance);
|
||||
|
||||
{
|
||||
wxWindow *parent;
|
||||
if (hwndParent) {
|
||||
// Create wxWidget-approved parent window.
|
||||
wxWindow parent;
|
||||
parent.SetHWND((WXHWND)(hwndParent ? hwndParent : GetForegroundWindow()));
|
||||
parent.AdoptAttributesFromHWND();
|
||||
wxTopLevelWindows.Append(&parent);
|
||||
parent = new wxWindow;
|
||||
parent->SetHWND((WXHWND)hwndParent);
|
||||
parent->AdoptAttributesFromHWND();
|
||||
wxTopLevelWindows.Append(parent);
|
||||
} else
|
||||
parent = NULL;
|
||||
|
||||
if (cfg.m_providers.size() > 1) {
|
||||
// Multiple identity providers: User has to select one first.
|
||||
wxEAPProviderSelectDialog dlg(cfg, &parent);
|
||||
if (cfg.m_providers.size() > 1) {
|
||||
// Multiple identity providers: User has to select one first.
|
||||
wxEAPProviderSelectDialog dlg(cfg, parent);
|
||||
|
||||
// Centre and display dialog.
|
||||
dlg.Centre(wxBOTH);
|
||||
if (!parent) {
|
||||
FLASHWINFO fwi = { sizeof(FLASHWINFO), dlg.GetHWND(), FLASHW_ALL | FLASHW_TIMERNOFG };
|
||||
::FlashWindowEx(&fwi);
|
||||
}
|
||||
if ((result = dlg.ShowModal()) == wxID_OK) {
|
||||
cfg_prov = dlg.GetSelection();
|
||||
assert(cfg_prov);
|
||||
}
|
||||
} else if (!cfg.m_providers.empty()) {
|
||||
// Single identity provider. No need to ask user to select one.
|
||||
result = wxID_OK;
|
||||
cfg_prov = &cfg.m_providers.front();
|
||||
} else {
|
||||
// No identity provider. Bail out.
|
||||
result = wxID_CANCEL;
|
||||
}
|
||||
|
||||
if (cfg_prov) {
|
||||
// The identity provider is selected.
|
||||
cfg_method = dynamic_cast<config_method_ttls*>(cfg_prov->m_methods.front().get());
|
||||
assert(cfg_method);
|
||||
|
||||
// Configure output credentials.
|
||||
cred_out.m_namespace = cfg_prov->m_namespace;
|
||||
cred_out.m_id = cfg_prov->m_id;
|
||||
auto cred = dynamic_cast<credentials_ttls*>(cfg_method->make_credentials());
|
||||
cred_out.m_cred.reset(cred);
|
||||
#if EAP_USE_NATIVE_CREDENTIAL_CACHE
|
||||
bool has_cached = cred_in.m_cred && cred_in.match(*cfg_prov);
|
||||
#endif
|
||||
|
||||
if (dwFlags & EAP_FLAG_GUEST_ACCESS) {
|
||||
// Disable credential saving for guests.
|
||||
cfg_method-> m_allow_save = false;
|
||||
cfg_method->m_inner->m_allow_save = false;
|
||||
}
|
||||
|
||||
// Combine outer credentials.
|
||||
wstring target_name(std::move(cfg_prov->get_id()));
|
||||
eap::credentials::source_t src_outer = cred->credentials_tls::combine(
|
||||
dwFlags,
|
||||
NULL,
|
||||
#if EAP_USE_NATIVE_CREDENTIAL_CACHE
|
||||
has_cached ? cred_in.m_cred.get() : NULL,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
*cfg_method,
|
||||
cfg_method->m_allow_save ? target_name.c_str() : NULL);
|
||||
if (src_outer == eap::credentials::source_unknown ||
|
||||
src_outer != eap::credentials::source_config && eap::config_method::status_cred_begin <= cfg_method->m_last_status && cfg_method->m_last_status < eap::config_method::status_cred_end)
|
||||
{
|
||||
// Build dialog to prompt for outer credentials.
|
||||
wxEAPCredentialsDialog dlg(*cfg_prov, parent);
|
||||
if (eap::config_method::status_cred_begin <= cfg_method->m_last_status && cfg_method->m_last_status < eap::config_method::status_cred_end)
|
||||
dlg.AddContent(new wxEAPCredentialWarningPanel(*cfg_prov, cfg_method->m_last_status, &dlg));
|
||||
auto panel = new wxTLSCredentialsPanel(*cfg_prov, *cfg_method, *cred, &dlg, false);
|
||||
panel->SetRemember(src_outer == eap::credentials::source_storage);
|
||||
dlg.AddContent(panel);
|
||||
|
||||
// Update dialog layout.
|
||||
dlg.Layout();
|
||||
dlg.GetSizer()->Fit(&dlg);
|
||||
|
||||
// Centre and display dialog.
|
||||
dlg.Centre(wxBOTH);
|
||||
if (!parent) {
|
||||
FLASHWINFO fwi = { sizeof(FLASHWINFO), dlg.GetHWND(), FLASHW_ALL | FLASHW_TIMERNOFG };
|
||||
::FlashWindowEx(&fwi);
|
||||
}
|
||||
if ((result = dlg.ShowModal()) == wxID_OK) {
|
||||
cfg_prov = dlg.GetSelection();
|
||||
assert(cfg_prov);
|
||||
// Write credentials to credential manager.
|
||||
if (panel->GetRemember()) {
|
||||
try {
|
||||
cred->credentials_tls::store(target_name.c_str(), 0);
|
||||
} catch (winstd::win_runtime_error &err) {
|
||||
wxLogError(winstd::tstring_printf(_("Error writing credentials to Credential Manager: %hs (error %u)"), err.what(), err.number()).c_str());
|
||||
} catch (...) {
|
||||
wxLogError(_("Writing credentials failed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!cfg.m_providers.empty()) {
|
||||
// Single identity provider. No need to ask user to select one.
|
||||
} else
|
||||
result = wxID_OK;
|
||||
cfg_prov = &cfg.m_providers.front();
|
||||
} else {
|
||||
// No identity provider. Bail out.
|
||||
result = wxID_CANCEL;
|
||||
}
|
||||
|
||||
if (cfg_prov) {
|
||||
// The identity provider is selected.
|
||||
cfg_method = dynamic_cast<config_method_ttls*>(cfg_prov->m_methods.front().get());
|
||||
assert(cfg_method);
|
||||
|
||||
// Configure output credentials.
|
||||
cred_out.m_namespace = cfg_prov->m_namespace;
|
||||
cred_out.m_id = cfg_prov->m_id;
|
||||
auto cred = dynamic_cast<credentials_ttls*>(cfg_method->make_credentials());
|
||||
cred_out.m_cred.reset(cred);
|
||||
#if EAP_USE_NATIVE_CREDENTIAL_CACHE
|
||||
bool has_cached = cred_in.m_cred && cred_in.match(*cfg_prov);
|
||||
#endif
|
||||
|
||||
if (dwFlags & EAP_FLAG_GUEST_ACCESS) {
|
||||
// Disable credential saving for guests.
|
||||
cfg_method-> m_allow_save = false;
|
||||
cfg_method->m_inner->m_allow_save = false;
|
||||
}
|
||||
|
||||
// Combine outer credentials.
|
||||
wstring target_name(std::move(cfg_prov->get_id()));
|
||||
eap::credentials::source_t src_outer = cred->credentials_tls::combine(
|
||||
if (result == wxID_OK) {
|
||||
// Combine inner credentials.
|
||||
eap::credentials::source_t src_inner = cred->m_inner->combine(
|
||||
dwFlags,
|
||||
NULL,
|
||||
#if EAP_USE_NATIVE_CREDENTIAL_CACHE
|
||||
has_cached ? cred_in.m_cred.get() : NULL,
|
||||
has_cached ? dynamic_cast<credentials_ttls*>(cred_in.m_cred.get())->m_inner.get() : NULL,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
*cfg_method,
|
||||
cfg_method->m_allow_save ? target_name.c_str() : NULL);
|
||||
if (src_outer == eap::credentials::source_unknown ||
|
||||
src_outer != eap::credentials::source_config && eap::config_method::status_cred_begin <= cfg_method->m_last_status && cfg_method->m_last_status < eap::config_method::status_cred_end)
|
||||
*cfg_method->m_inner,
|
||||
cfg_method->m_inner->m_allow_save ? target_name.c_str() : NULL);
|
||||
if (src_inner == eap::credentials::source_unknown ||
|
||||
src_inner != eap::credentials::source_config && eap::config_method::status_cred_begin <= cfg_method->m_inner->m_last_status && cfg_method->m_inner->m_last_status < eap::config_method::status_cred_end)
|
||||
{
|
||||
// Build dialog to prompt for outer credentials.
|
||||
wxEAPCredentialsDialog dlg(*cfg_prov, &parent);
|
||||
if (eap::config_method::status_cred_begin <= cfg_method->m_last_status && cfg_method->m_last_status < eap::config_method::status_cred_end)
|
||||
dlg.AddContent(new wxEAPCredentialWarningPanel(*cfg_prov, cfg_method->m_last_status, &dlg));
|
||||
auto panel = new wxTLSCredentialsPanel(*cfg_prov, *cfg_method, *cred, &dlg, false);
|
||||
panel->SetRemember(src_outer == eap::credentials::source_storage);
|
||||
dlg.AddContent(panel);
|
||||
// Prompt for inner credentials.
|
||||
#if EAP_INNER_EAPHOST
|
||||
auto cfg_inner_eaphost = dynamic_cast<config_method_eaphost*>(cfg_method->m_inner.get());
|
||||
if (!cfg_inner_eaphost)
|
||||
#endif
|
||||
{
|
||||
// Native inner methods. Build dialog to prompt for inner credentials.
|
||||
wxEAPCredentialsDialog dlg(*cfg_prov, parent);
|
||||
if (eap::config_method::status_cred_begin <= cfg_method->m_inner->m_last_status && cfg_method->m_inner->m_last_status < eap::config_method::status_cred_end)
|
||||
dlg.AddContent(new wxEAPCredentialWarningPanel(*cfg_prov, cfg_method->m_inner->m_last_status, &dlg));
|
||||
wxEAPCredentialsPanelBase *panel = NULL;
|
||||
switch (cfg_method->m_inner->get_method_id()) {
|
||||
case eap_type_legacy_pap : panel = new wxPAPCredentialsPanel (*cfg_prov, *dynamic_cast<const eap::config_method_pap *>(cfg_method->m_inner.get()), *dynamic_cast<eap::credentials_pass *>(cred->m_inner.get()), &dlg, false); break;
|
||||
case eap_type_legacy_mschapv2: panel = new wxMSCHAPv2CredentialsPanel(*cfg_prov, *dynamic_cast<const eap::config_method_mschapv2 *>(cfg_method->m_inner.get()), *dynamic_cast<eap::credentials_pass *>(cred->m_inner.get()), &dlg, false); break;
|
||||
case eap_type_mschapv2 : panel = new wxMSCHAPv2CredentialsPanel(*cfg_prov, *dynamic_cast<const eap::config_method_eapmschapv2*>(cfg_method->m_inner.get()), *dynamic_cast<eap::credentials_pass *>(cred->m_inner.get()), &dlg, false); break;
|
||||
case eap_type_gtc : {
|
||||
// EAP-GTC credential prompt differes for "Challenge/Response" and "Password" authentication modes.
|
||||
eap::credentials_identity *cred_resp;
|
||||
eap::credentials_pass *cred_pass;
|
||||
if ((cred_resp = dynamic_cast<eap::credentials_identity*>(cred->m_inner.get())) != NULL)
|
||||
panel = new wxGTCResponseCredentialsPanel(*cfg_prov, *dynamic_cast<const eap::config_method_eapgtc*>(cfg_method->m_inner.get()), *cred_resp, &dlg, false);
|
||||
else if ((cred_pass = dynamic_cast<eap::credentials_pass*>(cred->m_inner.get())) != NULL)
|
||||
panel = new wxGTCPasswordCredentialsPanel(*cfg_prov, *dynamic_cast<const eap::config_method_eapgtc*>(cfg_method->m_inner.get()), *cred_pass, &dlg, false);
|
||||
else
|
||||
wxLogError("Unsupported authentication mode.");
|
||||
break;
|
||||
}
|
||||
default : wxLogError("Unsupported inner authentication method.");
|
||||
}
|
||||
panel->SetRemember(src_inner == eap::credentials::source_storage);
|
||||
dlg.AddContent(panel);
|
||||
|
||||
// Update dialog layout.
|
||||
dlg.Layout();
|
||||
dlg.GetSizer()->Fit(&dlg);
|
||||
// Update dialog layout.
|
||||
dlg.Layout();
|
||||
dlg.GetSizer()->Fit(&dlg);
|
||||
|
||||
// Centre and display dialog.
|
||||
dlg.Centre(wxBOTH);
|
||||
if ((result = dlg.ShowModal()) == wxID_OK) {
|
||||
// Write credentials to credential manager.
|
||||
if (panel->GetRemember()) {
|
||||
try {
|
||||
cred->credentials_tls::store(target_name.c_str(), 0);
|
||||
} catch (winstd::win_runtime_error &err) {
|
||||
wxLogError(winstd::tstring_printf(_("Error writing credentials to Credential Manager: %hs (error %u)"), err.what(), err.number()).c_str());
|
||||
} catch (...) {
|
||||
wxLogError(_("Writing credentials failed."));
|
||||
// Centre and display dialog.
|
||||
dlg.Centre(wxBOTH);
|
||||
if (!parent) {
|
||||
FLASHWINFO fwi = { sizeof(FLASHWINFO), dlg.GetHWND(), FLASHW_ALL | FLASHW_TIMERNOFG };
|
||||
::FlashWindowEx(&fwi);
|
||||
}
|
||||
if ((result = dlg.ShowModal()) == wxID_OK) {
|
||||
// Write credentials to credential manager.
|
||||
if (panel->GetRemember()) {
|
||||
try {
|
||||
cred->m_inner->store(target_name.c_str(), 1);
|
||||
} catch (winstd::win_runtime_error &err) {
|
||||
wxLogError(winstd::tstring_printf(_("Error writing credentials to Credential Manager: %hs (error %u)"), err.what(), err.number()).c_str());
|
||||
} catch (...) {
|
||||
wxLogError(_("Writing credentials failed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if EAP_INNER_EAPHOST
|
||||
else {
|
||||
// EapHost inner method
|
||||
auto cred_inner = dynamic_cast<eap::credentials_eaphost*>(cred->m_inner.get());
|
||||
DWORD cred_data_size = 0;
|
||||
winstd::eap_blob cred_data;
|
||||
unique_ptr<WCHAR[], EapHostPeerFreeMemory_delete> identity;
|
||||
winstd::eap_error error;
|
||||
DWORD dwResult = EapHostPeerInvokeIdentityUI(
|
||||
0,
|
||||
cfg_inner_eaphost->get_type(),
|
||||
dwFlags,
|
||||
hwndParent,
|
||||
(DWORD)cfg_inner_eaphost->m_cfg_blob.size(), cfg_inner_eaphost->m_cfg_blob.data(),
|
||||
(DWORD)cred_inner->m_cred_blob.size(), cred_inner->m_cred_blob.data(),
|
||||
&cred_data_size, &cred_data._Myptr,
|
||||
&identity._Myptr,
|
||||
&error._Myptr,
|
||||
NULL);
|
||||
result = dwResult == ERROR_SUCCESS ? wxID_OK : wxID_CANCEL;
|
||||
if (dwResult == ERROR_SUCCESS) {
|
||||
// Inner EAP method provided credentials.
|
||||
cred_inner->m_identity = identity.get();
|
||||
cred_inner->m_cred_blob.assign(cred_data.get(), cred_data.get() + cred_data_size);
|
||||
SecureZeroMemory(cred_data.get(), cred_data_size);
|
||||
|
||||
// TODO: If we ever choose to store EapHost credentials to Windows Credential Manager, add a "Save credentials? Yes/No" prompt here and write them to Credential Manager.
|
||||
} else if (dwResult == ERROR_CANCELLED) {
|
||||
// Not really an error.
|
||||
} else if (error)
|
||||
wxLogError(_("Invoking EAP identity UI failed (error %u, %s, %s)."), error->dwWinError, error->pRootCauseString, error->pRepairString);
|
||||
else
|
||||
wxLogError(_("Invoking EAP identity UI failed (error %u)."), dwResult);
|
||||
}
|
||||
#endif
|
||||
} else
|
||||
result = wxID_OK;
|
||||
|
||||
if (result == wxID_OK) {
|
||||
// Combine inner credentials.
|
||||
eap::credentials::source_t src_inner = cred->m_inner->combine(
|
||||
dwFlags,
|
||||
NULL,
|
||||
#if EAP_USE_NATIVE_CREDENTIAL_CACHE
|
||||
has_cached ? dynamic_cast<credentials_ttls*>(cred_in.m_cred.get())->m_inner.get() : NULL,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
*cfg_method->m_inner,
|
||||
cfg_method->m_inner->m_allow_save ? target_name.c_str() : NULL);
|
||||
if (src_inner == eap::credentials::source_unknown ||
|
||||
src_inner != eap::credentials::source_config && eap::config_method::status_cred_begin <= cfg_method->m_inner->m_last_status && cfg_method->m_inner->m_last_status < eap::config_method::status_cred_end)
|
||||
{
|
||||
// Prompt for inner credentials.
|
||||
#if EAP_INNER_EAPHOST
|
||||
auto cfg_inner_eaphost = dynamic_cast<config_method_eaphost*>(cfg_method->m_inner.get());
|
||||
if (!cfg_inner_eaphost)
|
||||
#endif
|
||||
{
|
||||
// Native inner methods. Build dialog to prompt for inner credentials.
|
||||
wxEAPCredentialsDialog dlg(*cfg_prov, &parent);
|
||||
if (eap::config_method::status_cred_begin <= cfg_method->m_inner->m_last_status && cfg_method->m_inner->m_last_status < eap::config_method::status_cred_end)
|
||||
dlg.AddContent(new wxEAPCredentialWarningPanel(*cfg_prov, cfg_method->m_inner->m_last_status, &dlg));
|
||||
wxEAPCredentialsPanelBase *panel = NULL;
|
||||
switch (cfg_method->m_inner->get_method_id()) {
|
||||
case eap_type_legacy_pap : panel = new wxPAPCredentialsPanel (*cfg_prov, *dynamic_cast<const eap::config_method_pap *>(cfg_method->m_inner.get()), *dynamic_cast<eap::credentials_pass *>(cred->m_inner.get()), &dlg, false); break;
|
||||
case eap_type_legacy_mschapv2: panel = new wxMSCHAPv2CredentialsPanel(*cfg_prov, *dynamic_cast<const eap::config_method_mschapv2 *>(cfg_method->m_inner.get()), *dynamic_cast<eap::credentials_pass *>(cred->m_inner.get()), &dlg, false); break;
|
||||
case eap_type_mschapv2 : panel = new wxMSCHAPv2CredentialsPanel(*cfg_prov, *dynamic_cast<const eap::config_method_eapmschapv2*>(cfg_method->m_inner.get()), *dynamic_cast<eap::credentials_pass *>(cred->m_inner.get()), &dlg, false); break;
|
||||
case eap_type_gtc : {
|
||||
// EAP-GTC credential prompt differes for "Challenge/Response" and "Password" authentication modes.
|
||||
eap::credentials_identity *cred_resp;
|
||||
eap::credentials_pass *cred_pass;
|
||||
if ((cred_resp = dynamic_cast<eap::credentials_identity*>(cred->m_inner.get())) != NULL)
|
||||
panel = new wxGTCResponseCredentialsPanel(*cfg_prov, *dynamic_cast<const eap::config_method_eapgtc*>(cfg_method->m_inner.get()), *cred_resp, &dlg, false);
|
||||
else if ((cred_pass = dynamic_cast<eap::credentials_pass*>(cred->m_inner.get())) != NULL)
|
||||
panel = new wxGTCPasswordCredentialsPanel(*cfg_prov, *dynamic_cast<const eap::config_method_eapgtc*>(cfg_method->m_inner.get()), *cred_pass, &dlg, false);
|
||||
else
|
||||
wxLogError("Unsupported authentication mode.");
|
||||
break;
|
||||
}
|
||||
default : wxLogError("Unsupported inner authentication method.");
|
||||
}
|
||||
panel->SetRemember(src_inner == eap::credentials::source_storage);
|
||||
dlg.AddContent(panel);
|
||||
|
||||
// Update dialog layout.
|
||||
dlg.Layout();
|
||||
dlg.GetSizer()->Fit(&dlg);
|
||||
|
||||
// Centre and display dialog.
|
||||
dlg.Centre(wxBOTH);
|
||||
if ((result = dlg.ShowModal()) == wxID_OK) {
|
||||
// Write credentials to credential manager.
|
||||
if (panel->GetRemember()) {
|
||||
try {
|
||||
cred->m_inner->store(target_name.c_str(), 1);
|
||||
} catch (winstd::win_runtime_error &err) {
|
||||
wxLogError(winstd::tstring_printf(_("Error writing credentials to Credential Manager: %hs (error %u)"), err.what(), err.number()).c_str());
|
||||
} catch (...) {
|
||||
wxLogError(_("Writing credentials failed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if EAP_INNER_EAPHOST
|
||||
else {
|
||||
// EapHost inner method
|
||||
auto cred_inner = dynamic_cast<eap::credentials_eaphost*>(cred->m_inner.get());
|
||||
DWORD cred_data_size = 0;
|
||||
winstd::eap_blob cred_data;
|
||||
unique_ptr<WCHAR[], EapHostPeerFreeMemory_delete> identity;
|
||||
winstd::eap_error error;
|
||||
DWORD dwResult = EapHostPeerInvokeIdentityUI(
|
||||
0,
|
||||
cfg_inner_eaphost->get_type(),
|
||||
dwFlags,
|
||||
hwndParent,
|
||||
(DWORD)cfg_inner_eaphost->m_cfg_blob.size(), cfg_inner_eaphost->m_cfg_blob.data(),
|
||||
(DWORD)cred_inner->m_cred_blob.size(), cred_inner->m_cred_blob.data(),
|
||||
&cred_data_size, &cred_data._Myptr,
|
||||
&identity._Myptr,
|
||||
&error._Myptr,
|
||||
NULL);
|
||||
result = dwResult == ERROR_SUCCESS ? wxID_OK : wxID_CANCEL;
|
||||
if (dwResult == ERROR_SUCCESS) {
|
||||
// Inner EAP method provided credentials.
|
||||
cred_inner->m_identity = identity.get();
|
||||
cred_inner->m_cred_blob.assign(cred_data.get(), cred_data.get() + cred_data_size);
|
||||
SecureZeroMemory(cred_data.get(), cred_data_size);
|
||||
|
||||
// TODO: If we ever choose to store EapHost credentials to Windows Credential Manager, add a "Save credentials? Yes/No" prompt here and write them to Credential Manager.
|
||||
} else if (dwResult == ERROR_CANCELLED) {
|
||||
// Not really an error.
|
||||
} else if (error)
|
||||
wxLogError(_("Invoking EAP identity UI failed (error %u, %s, %s)."), error->dwWinError, error->pRootCauseString, error->pRepairString);
|
||||
else
|
||||
wxLogError(_("Invoking EAP identity UI failed (error %u)."), dwResult);
|
||||
}
|
||||
#endif
|
||||
} else
|
||||
result = wxID_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxTopLevelWindows.DeleteObject(&parent);
|
||||
parent.SetHWND((WXHWND)NULL);
|
||||
if (parent) {
|
||||
wxTopLevelWindows.DeleteObject(parent);
|
||||
parent->SetHWND((WXHWND)NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,39 +461,47 @@ void eap::peer_ttls_ui::invoke_interactive_ui(
|
||||
// Initialize application.
|
||||
wxInitializerPeer init(m_instance);
|
||||
|
||||
{
|
||||
wxWindow *parent;
|
||||
if (hwndParent) {
|
||||
// Create wxWidget-approved parent window.
|
||||
wxWindow parent;
|
||||
parent.SetHWND((WXHWND)(hwndParent ? hwndParent : GetForegroundWindow()));
|
||||
parent.AdoptAttributesFromHWND();
|
||||
wxTopLevelWindows.Append(&parent);
|
||||
parent = new wxWindow;
|
||||
parent->SetHWND((WXHWND)hwndParent);
|
||||
parent->AdoptAttributesFromHWND();
|
||||
wxTopLevelWindows.Append(parent);
|
||||
} else
|
||||
parent = NULL;
|
||||
|
||||
{
|
||||
sanitizing_wstring
|
||||
challenge(reinterpret_cast<sanitizing_wstring::const_pointer>(ctx.m_data.data()), ctx.m_data.size()/sizeof(sanitizing_wstring::value_type)),
|
||||
response;
|
||||
{
|
||||
sanitizing_wstring
|
||||
challenge(reinterpret_cast<sanitizing_wstring::const_pointer>(ctx.m_data.data()), ctx.m_data.size()/sizeof(sanitizing_wstring::value_type)),
|
||||
response;
|
||||
|
||||
// Build dialog to prompt for response.
|
||||
wxGTCResponseDialog dlg(*cfg_prov, &parent);
|
||||
auto panel = new wxGTCResponsePanel(response, challenge.c_str(), &dlg);
|
||||
dlg.AddContent(panel);
|
||||
// Build dialog to prompt for response.
|
||||
wxGTCResponseDialog dlg(*cfg_prov, parent);
|
||||
auto panel = new wxGTCResponsePanel(response, challenge.c_str(), &dlg);
|
||||
dlg.AddContent(panel);
|
||||
|
||||
// Update dialog layout.
|
||||
dlg.Layout();
|
||||
dlg.GetSizer()->Fit(&dlg);
|
||||
// Update dialog layout.
|
||||
dlg.Layout();
|
||||
dlg.GetSizer()->Fit(&dlg);
|
||||
|
||||
// Centre and display dialog.
|
||||
dlg.Centre(wxBOTH);
|
||||
if ((result = dlg.ShowModal()) == wxID_OK) {
|
||||
// Save response.
|
||||
ctx.m_data.assign(
|
||||
reinterpret_cast<sanitizing_blob::const_pointer>(response.data() ),
|
||||
reinterpret_cast<sanitizing_blob::const_pointer>(response.data() + response.length()));
|
||||
}
|
||||
// Centre and display dialog.
|
||||
dlg.Centre(wxBOTH);
|
||||
if (!parent) {
|
||||
FLASHWINFO fwi = { sizeof(FLASHWINFO), dlg.GetHWND(), FLASHW_ALL | FLASHW_TIMERNOFG };
|
||||
::FlashWindowEx(&fwi);
|
||||
}
|
||||
if ((result = dlg.ShowModal()) == wxID_OK) {
|
||||
// Save response.
|
||||
ctx.m_data.assign(
|
||||
reinterpret_cast<sanitizing_blob::const_pointer>(response.data() ),
|
||||
reinterpret_cast<sanitizing_blob::const_pointer>(response.data() + response.length()));
|
||||
}
|
||||
}
|
||||
|
||||
wxTopLevelWindows.DeleteObject(&parent);
|
||||
parent.SetHWND((WXHWND)NULL);
|
||||
if (parent) {
|
||||
wxTopLevelWindows.DeleteObject(parent);
|
||||
parent->SetHWND((WXHWND)NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,9 +558,10 @@ wxInitializerPeer::wxInitializerPeer(_In_ HINSTANCE instance)
|
||||
|
||||
// Do our wxWidgets configuration and localization initialization.
|
||||
wxInitializeConfig();
|
||||
if (wxInitializeLocale(s_locale)) {
|
||||
s_locale.AddCatalog(wxT("wxExtend") wxT(wxExtendVersion));
|
||||
s_locale.AddCatalog(wxT("EAPTTLSUI"));
|
||||
s_locale = new wxLocale;
|
||||
if (wxInitializeLocale(*s_locale)) {
|
||||
s_locale->AddCatalog(wxT("wxExtend") wxT(wxExtendVersion));
|
||||
s_locale->AddCatalog(wxT("EAPTTLSUI"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,9 +573,14 @@ wxInitializerPeer::~wxInitializerPeer()
|
||||
return;
|
||||
|
||||
wxEntryCleanup();
|
||||
|
||||
if (s_locale) {
|
||||
delete s_locale;
|
||||
s_locale = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wxCriticalSection wxInitializerPeer::s_lock;
|
||||
unsigned long wxInitializerPeer::s_init_ref_count = 0;
|
||||
wxLocale wxInitializerPeer::s_locale;
|
||||
wxLocale *wxInitializerPeer::s_locale = NULL;
|
||||
|
Submodule lib/WinStd updated: 905fd066dc...750f40fada
Submodule lib/wxExtend updated: eb33a877d1...5ff1a95df2
Reference in New Issue
Block a user