blob: 6e6deae63539eac3a004f585f35b9992024ba5c9 [file] [log] [blame]
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2011-2018 Heiko Hund <heiko.hund@sophos.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "service.h"
#include "validate.h"
LPCTSTR service_instance = TEXT("");
static wchar_t win_sys_path[MAX_PATH];
/*
* These are necessary due to certain buggy implementations of (v)snprintf,
* that don't guarantee null termination for size > 0.
*/
BOOL
openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
{
int len = -1;
if (size > 0)
{
len = _vsntprintf(str, size, format, arglist);
str[size - 1] = 0;
}
return (len >= 0 && (size_t)len < size);
}
BOOL
openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format, ...)
{
va_list arglist;
BOOL res = FALSE;
if (size > 0)
{
va_start(arglist, format);
res = openvpn_vsntprintf(str, size, format, arglist);
va_end(arglist);
}
return res;
}
BOOL
openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format, ...)
{
va_list arglist;
int len = -1;
if (size > 0)
{
va_start(arglist, format);
len = vswprintf(str, size, format, arglist);
va_end(arglist);
str[size - 1] = L'\0';
}
return (len >= 0 && len < size);
}
static DWORD
GetRegString(HKEY key, LPCTSTR value, LPTSTR data, DWORD size, LPCTSTR default_value)
{
LONG status = RegGetValue(key, NULL, value, RRF_RT_REG_SZ,
NULL, (LPBYTE) data, &size);
if (status == ERROR_FILE_NOT_FOUND && default_value)
{
size_t len = size/sizeof(data[0]);
if (openvpn_sntprintf(data, len, default_value))
{
status = ERROR_SUCCESS;
}
}
if (status != ERROR_SUCCESS)
{
SetLastError(status);
return MsgToEventLog(M_SYSERR, TEXT("Error querying registry value: HKLM\\SOFTWARE\\" PACKAGE_NAME "%s\\%s"), service_instance, value);
}
return ERROR_SUCCESS;
}
DWORD
GetOpenvpnSettings(settings_t *s)
{
TCHAR reg_path[256];
TCHAR priority[64];
TCHAR append[2];
DWORD error;
HKEY key;
TCHAR install_path[MAX_PATH];
TCHAR default_value[MAX_PATH];
openvpn_sntprintf(reg_path, _countof(reg_path), TEXT("SOFTWARE\\" PACKAGE_NAME "%s"), service_instance);
LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_path, 0, KEY_READ, &key);
if (status != ERROR_SUCCESS)
{
SetLastError(status);
return MsgToEventLog(M_SYSERR, TEXT("Could not open Registry key HKLM\\%s not found"), reg_path);
}
/* The default value of REG_KEY is the install path */
status = GetRegString(key, NULL, install_path, sizeof(install_path), NULL);
if (status != ERROR_SUCCESS)
{
error = status;
goto out;
}
openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\bin\\openvpn.exe"),
install_path);
error = GetRegString(key, TEXT("exe_path"), s->exe_path, sizeof(s->exe_path), default_value);
if (error != ERROR_SUCCESS)
{
goto out;
}
openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\config"), install_path);
error = GetRegString(key, TEXT("config_dir"), s->config_dir, sizeof(s->config_dir),
default_value);
if (error != ERROR_SUCCESS)
{
goto out;
}
error = GetRegString(key, TEXT("config_ext"), s->ext_string, sizeof(s->ext_string),
TEXT(".ovpn"));
if (error != ERROR_SUCCESS)
{
goto out;
}
openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\log"), install_path);
error = GetRegString(key, TEXT("log_dir"), s->log_dir, sizeof(s->log_dir), default_value);
if (error != ERROR_SUCCESS)
{
goto out;
}
error = GetRegString(key, TEXT("priority"), priority, sizeof(priority),
TEXT("NORMAL_PRIORITY_CLASS"));
if (error != ERROR_SUCCESS)
{
goto out;
}
error = GetRegString(key, TEXT("log_append"), append, sizeof(append), TEXT("0"));
if (error != ERROR_SUCCESS)
{
goto out;
}
/* read if present, else use default */
error = GetRegString(key, TEXT("ovpn_admin_group"), s->ovpn_admin_group,
sizeof(s->ovpn_admin_group), OVPN_ADMIN_GROUP);
if (error != ERROR_SUCCESS)
{
goto out;
}
/* set process priority */
if (!_tcsicmp(priority, TEXT("IDLE_PRIORITY_CLASS")))
{
s->priority = IDLE_PRIORITY_CLASS;
}
else if (!_tcsicmp(priority, TEXT("BELOW_NORMAL_PRIORITY_CLASS")))
{
s->priority = BELOW_NORMAL_PRIORITY_CLASS;
}
else if (!_tcsicmp(priority, TEXT("NORMAL_PRIORITY_CLASS")))
{
s->priority = NORMAL_PRIORITY_CLASS;
}
else if (!_tcsicmp(priority, TEXT("ABOVE_NORMAL_PRIORITY_CLASS")))
{
s->priority = ABOVE_NORMAL_PRIORITY_CLASS;
}
else if (!_tcsicmp(priority, TEXT("HIGH_PRIORITY_CLASS")))
{
s->priority = HIGH_PRIORITY_CLASS;
}
else
{
SetLastError(ERROR_INVALID_DATA);
error = MsgToEventLog(M_SYSERR, TEXT("Unknown priority name: %s"), priority);
goto out;
}
/* set log file append/truncate flag */
if (append[0] == TEXT('0'))
{
s->append = FALSE;
}
else if (append[0] == TEXT('1'))
{
s->append = TRUE;
}
else
{
SetLastError(ERROR_INVALID_DATA);
error = MsgToEventLog(M_ERR, TEXT("Log file append flag (given as '%s') must be '0' or '1'"), append);
goto out;
}
out:
RegCloseKey(key);
return error;
}
LPCTSTR
GetLastErrorText()
{
DWORD error;
static TCHAR buf[256];
DWORD len;
LPTSTR tmp = NULL;
error = GetLastError();
len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL, error, LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL);
if (len == 0 || (long) _countof(buf) < (long) len + 14)
{
buf[0] = TEXT('\0');
}
else
{
tmp[_tcslen(tmp) - 2] = TEXT('\0'); /* remove CR/LF characters */
openvpn_sntprintf(buf, _countof(buf), TEXT("%s (0x%x)"), tmp, error);
}
if (tmp)
{
LocalFree(tmp);
}
return buf;
}
DWORD
MsgToEventLog(DWORD flags, LPCTSTR format, ...)
{
HANDLE hEventSource;
TCHAR msg[2][256];
DWORD error = 0;
LPCTSTR err_msg = TEXT("");
va_list arglist;
if (flags & MSG_FLAGS_SYS_CODE)
{
error = GetLastError();
err_msg = GetLastErrorText();
}
hEventSource = RegisterEventSource(NULL, APPNAME);
if (hEventSource != NULL)
{
openvpn_sntprintf(msg[0], _countof(msg[0]),
TEXT("%s%s%s: %s"), APPNAME, service_instance,
(flags & MSG_FLAGS_ERROR) ? TEXT(" error") : TEXT(""), err_msg);
va_start(arglist, format);
openvpn_vsntprintf(msg[1], _countof(msg[1]), format, arglist);
va_end(arglist);
const TCHAR *mesg[] = { msg[0], msg[1] };
ReportEvent(hEventSource, flags & MSG_FLAGS_ERROR ?
EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
0, 0, NULL, 2, 0, mesg, NULL);
DeregisterEventSource(hEventSource);
}
return error;
}
/* Convert a utf8 string to utf16. Caller should free the result */
wchar_t *
utf8to16(const char *utf8)
{
int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
wchar_t *utf16 = malloc(n * sizeof(wchar_t));
if (!utf16)
{
return NULL;
}
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, n);
return utf16;
}
const wchar_t *
get_win_sys_path(void)
{
const wchar_t *default_sys_path = L"C:\\Windows\\system32";
if (!GetSystemDirectoryW(win_sys_path, _countof(win_sys_path)))
{
wcsncpy(win_sys_path, default_sys_path, _countof(win_sys_path));
win_sys_path[_countof(win_sys_path) - 1] = L'\0';
}
return win_sys_path;
}