src/openvpnserv/common.c
a24dd2e3
 /*
  *  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.
  *
49979459
  *  Copyright (C) 2011-2018 Heiko Hund <heiko.hund@sophos.com>
a24dd2e3
  *
  *  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.
  *
caa54ac3
  *  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.
a24dd2e3
  */
 
d2a7415f
 #include "service.h"
 #include "validate.h"
 
f3fec49b
 LPCTSTR service_instance = TEXT("");
4eb46553
 static wchar_t win_sys_path[MAX_PATH];
f3fec49b
 
a24dd2e3
 /*
  * These are necessary due to certain buggy implementations of (v)snprintf,
  * that don't guarantee null termination for size > 0.
  */
ab9193c3
 BOOL
81d882d5
 openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
a24dd2e3
 {
81d882d5
     int len = -1;
     if (size > 0)
a24dd2e3
     {
81d882d5
         len = _vsntprintf(str, size, format, arglist);
         str[size - 1] = 0;
a24dd2e3
     }
f755c992
     return (len >= 0 && (size_t)len < size);
a24dd2e3
 }
ab9193c3
 
 BOOL
81d882d5
 openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format, ...)
a24dd2e3
 {
81d882d5
     va_list arglist;
ab9193c3
     BOOL res = FALSE;
81d882d5
     if (size > 0)
a24dd2e3
     {
81d882d5
         va_start(arglist, format);
ab9193c3
         res = openvpn_vsntprintf(str, size, format, arglist);
81d882d5
         va_end(arglist);
a24dd2e3
     }
ab9193c3
     return res;
a24dd2e3
 }
 
43a5a4f3
 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);
 }
 
a24dd2e3
 static DWORD
b1263b06
 GetRegString(HKEY key, LPCTSTR value, LPTSTR data, DWORD size, LPCTSTR default_value)
a24dd2e3
 {
7de0ee4f
     LONG status = RegGetValue(key, NULL, value, RRF_RT_REG_SZ,
b1263b06
                               NULL, (LPBYTE) data, &size);
a24dd2e3
 
b1263b06
     if (status == ERROR_FILE_NOT_FOUND && default_value)
81d882d5
     {
b1263b06
         size_t len = size/sizeof(data[0]);
ab9193c3
         if (openvpn_sntprintf(data, len, default_value))
b1263b06
         {
             status = ERROR_SUCCESS;
         }
81d882d5
     }
a24dd2e3
 
81d882d5
     if (status != ERROR_SUCCESS)
a24dd2e3
     {
81d882d5
         SetLastError(status);
f3fec49b
         return MsgToEventLog(M_SYSERR, TEXT("Error querying registry value: HKLM\\SOFTWARE\\" PACKAGE_NAME "%s\\%s"), service_instance, value);
a24dd2e3
     }
 
81d882d5
     return ERROR_SUCCESS;
a24dd2e3
 }
 
 
 DWORD
81d882d5
 GetOpenvpnSettings(settings_t *s)
a24dd2e3
 {
f3fec49b
     TCHAR reg_path[256];
81d882d5
     TCHAR priority[64];
     TCHAR append[2];
     DWORD error;
     HKEY key;
db04bca6
     TCHAR install_path[MAX_PATH];
     TCHAR default_value[MAX_PATH];
81d882d5
 
f3fec49b
     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);
81d882d5
     if (status != ERROR_SUCCESS)
     {
         SetLastError(status);
f3fec49b
         return MsgToEventLog(M_SYSERR, TEXT("Could not open Registry key HKLM\\%s not found"), reg_path);
81d882d5
     }
 
db04bca6
     /* The default value of REG_KEY is the install path */
     if (GetRegString(key, NULL, install_path, sizeof(install_path), NULL) != ERROR_SUCCESS)
     {
         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);
81d882d5
     if (error != ERROR_SUCCESS)
     {
         goto out;
     }
 
db04bca6
     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);
81d882d5
     if (error != ERROR_SUCCESS)
     {
         goto out;
     }
 
db04bca6
     error = GetRegString(key, TEXT("config_ext"), s->ext_string, sizeof(s->ext_string),
                          TEXT(".ovpn"));
81d882d5
     if (error != ERROR_SUCCESS)
     {
         goto out;
     }
 
db04bca6
     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);
81d882d5
     if (error != ERROR_SUCCESS)
     {
         goto out;
     }
 
db04bca6
     error = GetRegString(key, TEXT("priority"), priority, sizeof(priority),
                          TEXT("NORMAL_PRIORITY_CLASS"));
81d882d5
     if (error != ERROR_SUCCESS)
     {
         goto out;
     }
 
db04bca6
     error = GetRegString(key, TEXT("log_append"), append, sizeof(append), TEXT("0"));
81d882d5
     if (error != ERROR_SUCCESS)
     {
         goto out;
     }
 
     /* read if present, else use default */
b1263b06
     error = GetRegString(key, TEXT("ovpn_admin_group"), s->ovpn_admin_group,
                          sizeof(s->ovpn_admin_group), OVPN_ADMIN_GROUP);
81d882d5
     if (error != ERROR_SUCCESS)
     {
b1263b06
         goto out;
81d882d5
     }
     /* 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;
a24dd2e3
     }
 
 out:
81d882d5
     RegCloseKey(key);
     return error;
a24dd2e3
 }
 
 
 LPCTSTR
81d882d5
 GetLastErrorText()
a24dd2e3
 {
81d882d5
     static TCHAR buf[256];
     DWORD len;
     LPTSTR tmp = NULL;
a24dd2e3
 
81d882d5
     len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                         NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL);
a24dd2e3
 
81d882d5
     if (len == 0 || (long) _countof(buf) < (long) len + 14)
     {
         buf[0] = TEXT('\0');
     }
     else
a24dd2e3
     {
81d882d5
         tmp[_tcslen(tmp) - 2] = TEXT('\0'); /* remove CR/LF characters */
         openvpn_sntprintf(buf, _countof(buf), TEXT("%s (0x%x)"), tmp, GetLastError());
a24dd2e3
     }
 
81d882d5
     if (tmp)
     {
         LocalFree(tmp);
     }
a24dd2e3
 
81d882d5
     return buf;
a24dd2e3
 }
 
 
 DWORD
81d882d5
 MsgToEventLog(DWORD flags, LPCTSTR format, ...)
a24dd2e3
 {
81d882d5
     HANDLE hEventSource;
     TCHAR msg[2][256];
     DWORD error = 0;
     LPCTSTR err_msg = TEXT("");
     va_list arglist;
a24dd2e3
 
81d882d5
     if (flags & MSG_FLAGS_SYS_CODE)
a24dd2e3
     {
81d882d5
         error = GetLastError();
         err_msg = GetLastErrorText();
a24dd2e3
     }
 
81d882d5
     hEventSource = RegisterEventSource(NULL, APPNAME);
     if (hEventSource != NULL)
a24dd2e3
     {
81d882d5
         openvpn_sntprintf(msg[0], _countof(msg[0]),
f3fec49b
                           TEXT("%s%s%s: %s"), APPNAME, service_instance,
81d882d5
                           (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);
a24dd2e3
     }
 
81d882d5
     return error;
a24dd2e3
 }
c098016a
 
 /* Convert a utf8 string to utf16. Caller should free the result */
 wchar_t *
81d882d5
 utf8to16(const char *utf8)
c098016a
 {
81d882d5
     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;
c098016a
 }
4eb46553
 
 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;
 }