mirror of https://github.com/OpenVPN/openvpn-gui
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
7.8 KiB
298 lines
7.8 KiB
/* |
|
* This file is a part of OpenVPN-GUI -- A Windows GUI for OpenVPN. |
|
* |
|
* Copyright (C) 2016 Selva Nair <selva.nair@gmail.com> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License 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 <windows.h> |
|
#include <wincrypt.h> |
|
|
|
#include "main.h" |
|
#include "registry.h" |
|
#include "save_pass.h" |
|
#include "misc.h" |
|
|
|
#define KEY_PASS_DATA L"key-data" |
|
#define AUTH_PASS_DATA L"auth-data" |
|
#define ENTROPY_DATA L"entropy" |
|
#define AUTH_USER_DATA L"username" |
|
#define ENTROPY_LEN 16 |
|
|
|
static DWORD |
|
crypt_protect(BYTE *data, int szdata, char *entropy, BYTE **out) |
|
{ |
|
DATA_BLOB data_out; |
|
DATA_BLOB data_in; |
|
DATA_BLOB e; |
|
|
|
data_in.pbData = data; |
|
data_in.cbData = szdata; |
|
e.pbData = (BYTE *) entropy; |
|
e.cbData = entropy ? strlen(entropy) : 0; |
|
|
|
if (CryptProtectData(&data_in, NULL, &e, NULL, NULL, 0, &data_out)) |
|
{ |
|
*out = data_out.pbData; |
|
return data_out.cbData; |
|
} |
|
PrintDebug(L"CryptProtectData failed (error = %lu)", GetLastError()); |
|
return 0; |
|
} |
|
|
|
static DWORD |
|
crypt_unprotect(BYTE *data, int szdata, char *entropy, BYTE **out) |
|
{ |
|
DATA_BLOB data_in; |
|
DATA_BLOB data_out = {0, 0}; |
|
DATA_BLOB e; |
|
|
|
data_in.pbData = data; |
|
data_in.cbData = szdata; |
|
e.pbData = (BYTE *) entropy; |
|
e.cbData = entropy ? strlen(entropy) : 0; |
|
|
|
if (CryptUnprotectData(&data_in, NULL, &e, NULL, NULL, 0, &data_out)) |
|
{ |
|
*out = data_out.pbData; |
|
return data_out.cbData; |
|
} |
|
else |
|
{ |
|
PrintDebug(L"CryptUnprotectData: decryption failed"); |
|
LocalFree(data_out.pbData); |
|
return 0; |
|
} |
|
} |
|
|
|
/* |
|
* If not found in registry and generate is true, a new nul terminated |
|
* random string is generated and saved in registry. |
|
* Else a zero-length string is returned and registry is not updated. |
|
*/ |
|
static void |
|
get_entropy(const WCHAR *config_name, char *e, int sz, BOOL generate) |
|
{ |
|
int len; |
|
|
|
len = GetConfigRegistryValue(config_name, ENTROPY_DATA, (BYTE *) e, sz); |
|
if (len > 0) |
|
{ |
|
e[len-1] = '\0'; |
|
PrintDebug(L"Got entropy from registry: %hs (len = %d)", e, len); |
|
return; |
|
} |
|
else if (generate && GetRandomPassword(e, sz)) |
|
{ |
|
e[sz-1] = '\0'; |
|
PrintDebug(L"Created new entropy string : %hs", e); |
|
if (SetConfigRegistryValueBinary(config_name, ENTROPY_DATA, (BYTE *)e, sz)) |
|
{ |
|
return; |
|
} |
|
} |
|
if (generate) |
|
{ |
|
PrintDebug(L"Failed to generate or save new entropy string -- using null string"); |
|
} |
|
*e = '\0'; |
|
return; |
|
} |
|
/* |
|
* Given a nul terminated string password, encrypt it and save in |
|
* a config specific registry key with specified name. |
|
* Returns 1 on success. |
|
*/ |
|
static int |
|
save_encrypted(const WCHAR *config_name, const WCHAR *password, const WCHAR *name) |
|
{ |
|
BYTE *out; |
|
DWORD len = (wcslen(password) + 1) * sizeof(WCHAR); |
|
char entropy[ENTROPY_LEN+1]; |
|
|
|
get_entropy(config_name, entropy, sizeof(entropy), true); |
|
len = crypt_protect((BYTE *) password, len, entropy, &out); |
|
if (len > 0) |
|
{ |
|
SetConfigRegistryValueBinary(config_name, name, out, len); |
|
LocalFree(out); |
|
return 1; |
|
} |
|
else |
|
{ |
|
return 0; |
|
} |
|
} |
|
|
|
/* |
|
* Encrypt the nul terminated string password and store it in the |
|
* registry with key name KEY_PASS_DATA. Returns 1 on success. |
|
*/ |
|
int |
|
SaveKeyPass(const WCHAR *config_name, const WCHAR *password) |
|
{ |
|
return save_encrypted(config_name, password, KEY_PASS_DATA); |
|
} |
|
|
|
/* |
|
* Encrypt the nul terminated string password and store it in the |
|
* registry with key name AUTH_PASS_DATA. Returns 1 on success. |
|
*/ |
|
int |
|
SaveAuthPass(const WCHAR *config_name, const WCHAR *password) |
|
{ |
|
return save_encrypted(config_name, password, AUTH_PASS_DATA); |
|
} |
|
|
|
/* |
|
* Returns 1 on success, 0 on failure. password should have space |
|
* for up to capacity wide chars incluing nul termination |
|
*/ |
|
static int |
|
recall_encrypted(const WCHAR *config_name, WCHAR *password, DWORD capacity, const WCHAR *name) |
|
{ |
|
BYTE in[2048]; |
|
BYTE *out; |
|
DWORD len; |
|
int retval = 0; |
|
char entropy[ENTROPY_LEN+1]; |
|
|
|
get_entropy(config_name, entropy, sizeof(entropy), false); |
|
|
|
memset(password, 0, capacity); |
|
|
|
len = GetConfigRegistryValue(config_name, name, in, sizeof(in)); |
|
if (len == 0) |
|
{ |
|
return 0; |
|
} |
|
|
|
len = crypt_unprotect(in, len, entropy, &out); |
|
if (len == 0) |
|
{ |
|
return 0; |
|
} |
|
|
|
if (len <= capacity * sizeof(*password)) |
|
{ |
|
memcpy(password, out, len); |
|
password[capacity-1] = L'\0'; /* in case the data was corrupted */ |
|
retval = 1; |
|
} |
|
else |
|
{ |
|
PrintDebug(L"recall_encrypted: saved '%ls' too long (len = %d bytes)", name, len); |
|
} |
|
|
|
SecureZeroMemory(out, len); |
|
LocalFree(out); |
|
|
|
return retval; |
|
} |
|
|
|
/* |
|
* Reccall saved private key password. The buffer password should be |
|
* have space for up to KEY_PASS_LEN WCHARs including nul. |
|
* Returns 1 on success, 0 on failure. |
|
*/ |
|
int |
|
RecallKeyPass(const WCHAR *config_name, WCHAR *password) |
|
{ |
|
return recall_encrypted(config_name, password, KEY_PASS_LEN, KEY_PASS_DATA); |
|
} |
|
|
|
/* |
|
* Reccall saved auth password. The buffer password should be |
|
* have space for up to USER_PASS_LEN WCHARs including nul. |
|
* Returns 1 on success, 0 on failure. |
|
*/ |
|
int |
|
RecallAuthPass(const WCHAR *config_name, WCHAR *password) |
|
{ |
|
return recall_encrypted(config_name, password, USER_PASS_LEN, AUTH_PASS_DATA); |
|
} |
|
|
|
int |
|
SaveUsername(const WCHAR *config_name, const WCHAR *username) |
|
{ |
|
DWORD len = (wcslen(username) + 1) * sizeof(*username); |
|
SetConfigRegistryValueBinary(config_name, AUTH_USER_DATA, (BYTE *) username, len); |
|
return 1; |
|
} |
|
/* |
|
* The buffer username should be have space for up to USER_PASS_LEN |
|
* WCHARs including nul. |
|
*/ |
|
int |
|
RecallUsername(const WCHAR *config_name, WCHAR *username) |
|
{ |
|
DWORD capacity = USER_PASS_LEN * sizeof(WCHAR); |
|
DWORD len; |
|
|
|
len = GetConfigRegistryValue(config_name, AUTH_USER_DATA, (BYTE *) username, capacity); |
|
if (len == 0) |
|
{ |
|
return 0; |
|
} |
|
username[USER_PASS_LEN-1] = L'\0'; |
|
return 1; |
|
} |
|
|
|
void |
|
DeleteSavedKeyPass(const WCHAR *config_name) |
|
{ |
|
DeleteConfigRegistryValue(config_name, KEY_PASS_DATA); |
|
} |
|
|
|
void |
|
DeleteSavedAuthPass(const WCHAR *config_name) |
|
{ |
|
DeleteConfigRegistryValue(config_name, AUTH_PASS_DATA); |
|
} |
|
|
|
/* delete saved config-specific auth password and private key passphrase */ |
|
void |
|
DeleteSavedPasswords(const WCHAR *config_name) |
|
{ |
|
DeleteConfigRegistryValue(config_name, KEY_PASS_DATA); |
|
DeleteConfigRegistryValue(config_name, AUTH_PASS_DATA); |
|
DeleteConfigRegistryValue(config_name, ENTROPY_DATA); |
|
} |
|
|
|
/* check if auth password is saved */ |
|
BOOL |
|
IsAuthPassSaved(const WCHAR *config_name) |
|
{ |
|
DWORD len = 0; |
|
len = GetConfigRegistryValue(config_name, AUTH_PASS_DATA, NULL, 0); |
|
PrintDebug(L"checking auth-pass-data in registry returned len = %d", len); |
|
return (len > 0); |
|
} |
|
|
|
/* check if key password is saved */ |
|
BOOL |
|
IsKeyPassSaved(const WCHAR *config_name) |
|
{ |
|
DWORD len = 0; |
|
len = GetConfigRegistryValue(config_name, KEY_PASS_DATA, NULL, 0); |
|
PrintDebug(L"checking key-pass-data in registry returned len = %d", len); |
|
return (len > 0); |
|
}
|
|
|