/* * 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 * * 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 */ 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); 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() { static TCHAR buf[256]; DWORD len; LPTSTR tmp = NULL; len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, GetLastError(), 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, GetLastError()); } 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; }