/* * 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-2025 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" LPCWSTR service_instance = L""; static wchar_t win_sys_path[MAX_PATH]; static DWORD GetRegString(HKEY key, LPCWSTR value, LPWSTR data, DWORD size, LPCWSTR 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 (swprintf(data, len, default_value)) { status = ERROR_SUCCESS; } } if (status != ERROR_SUCCESS) { SetLastError(status); return MsgToEventLog(M_SYSERR, L"Error querying registry value: HKLM\\SOFTWARE\\" _L(PACKAGE_NAME) L"%ls\\%ls", service_instance, value); } return ERROR_SUCCESS; } DWORD GetOpenvpnSettings(settings_t *s) { WCHAR reg_path[256]; WCHAR priority[64]; WCHAR append[2]; DWORD error; HKEY key; WCHAR install_path[MAX_PATH]; WCHAR default_value[MAX_PATH]; swprintf(reg_path, _countof(reg_path), L"SOFTWARE\\" _L(PACKAGE_NAME) L"%ls", service_instance); LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_path, 0, KEY_READ, &key); if (status != ERROR_SUCCESS) { SetLastError(status); return MsgToEventLog(M_SYSERR, L"Could not open Registry key HKLM\\%ls 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; } swprintf(default_value, _countof(default_value), L"%ls\\bin\\openvpn.exe", install_path); error = GetRegString(key, L"exe_path", s->exe_path, sizeof(s->exe_path), default_value); if (error != ERROR_SUCCESS) { goto out; } swprintf(default_value, _countof(default_value), L"%ls\\config", install_path); error = GetRegString(key, L"config_dir", s->config_dir, sizeof(s->config_dir), default_value); if (error != ERROR_SUCCESS) { goto out; } swprintf(default_value, _countof(default_value), L"%ls\\bin", install_path); error = GetRegString(key, L"bin_dir", s->bin_dir, sizeof(s->bin_dir), default_value); if (error != ERROR_SUCCESS) { goto out; } error = GetRegString(key, L"config_ext", s->ext_string, sizeof(s->ext_string), L".ovpn"); if (error != ERROR_SUCCESS) { goto out; } swprintf(default_value, _countof(default_value), L"%ls\\log", install_path); error = GetRegString(key, L"log_dir", s->log_dir, sizeof(s->log_dir), default_value); if (error != ERROR_SUCCESS) { goto out; } error = GetRegString(key, L"priority", priority, sizeof(priority), L"NORMAL_PRIORITY_CLASS"); if (error != ERROR_SUCCESS) { goto out; } error = GetRegString(key, L"log_append", append, sizeof(append), L"0"); if (error != ERROR_SUCCESS) { goto out; } /* read if present, else use default */ error = GetRegString(key, L"ovpn_admin_group", s->ovpn_admin_group, sizeof(s->ovpn_admin_group), OVPN_ADMIN_GROUP); if (error != ERROR_SUCCESS) { goto out; } error = GetRegString(key, L"ovpn_service_user", s->ovpn_service_user, sizeof(s->ovpn_service_user), OVPN_SERVICE_USER); if (error != ERROR_SUCCESS) { goto out; } /* set process priority */ if (!_wcsicmp(priority, L"IDLE_PRIORITY_CLASS")) { s->priority = IDLE_PRIORITY_CLASS; } else if (!_wcsicmp(priority, L"BELOW_NORMAL_PRIORITY_CLASS")) { s->priority = BELOW_NORMAL_PRIORITY_CLASS; } else if (!_wcsicmp(priority, L"NORMAL_PRIORITY_CLASS")) { s->priority = NORMAL_PRIORITY_CLASS; } else if (!_wcsicmp(priority, L"ABOVE_NORMAL_PRIORITY_CLASS")) { s->priority = ABOVE_NORMAL_PRIORITY_CLASS; } else if (!_wcsicmp(priority, L"HIGH_PRIORITY_CLASS")) { s->priority = HIGH_PRIORITY_CLASS; } else { SetLastError(ERROR_INVALID_DATA); error = MsgToEventLog(M_SYSERR, L"Unknown priority name: %ls", priority); goto out; } /* set log file append/truncate flag */ if (append[0] == L'0') { s->append = FALSE; } else if (append[0] == L'1') { s->append = TRUE; } else { SetLastError(ERROR_INVALID_DATA); error = MsgToEventLog(M_ERR, L"Log file append flag (given as '%ls') must be '0' or '1'", append); goto out; } out: RegCloseKey(key); return error; } LPCWSTR GetLastErrorText(void) { DWORD error; static WCHAR buf[256]; DWORD len; LPWSTR tmp = NULL; error = GetLastError(); len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, error, LANG_NEUTRAL, tmp, 0, NULL); if (len == 0 || (long) _countof(buf) < (long) len + 14) { buf[0] = L'\0'; } else { tmp[wcslen(tmp) - 2] = L'\0'; /* remove CR/LF characters */ swprintf(buf, _countof(buf), L"%ls (0x%x)", tmp, error); } if (tmp) { LocalFree(tmp); } return buf; } DWORD MsgToEventLog(DWORD flags, LPCWSTR format, ...) { HANDLE hEventSource; WCHAR msg[2][256]; DWORD error = 0; LPCWSTR err_msg = L""; va_list arglist; if (flags & MSG_FLAGS_SYS_CODE) { error = GetLastError(); err_msg = GetLastErrorText(); } hEventSource = RegisterEventSource(NULL, APPNAME); if (hEventSource != NULL) { swprintf(msg[0], _countof(msg[0]), L"%ls%ls%ls: %ls", APPNAME, service_instance, (flags & MSG_FLAGS_ERROR) ? L" error" : L"", err_msg); va_start(arglist, format); vswprintf(msg[1], _countof(msg[1]), format, arglist); va_end(arglist); const WCHAR *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; } wchar_t * utf8to16_size(const char *utf8, int size) { int n = MultiByteToWideChar(CP_UTF8, 0, utf8, size, NULL, 0); wchar_t *utf16 = malloc(n * sizeof(wchar_t)); if (!utf16) { return NULL; } MultiByteToWideChar(CP_UTF8, 0, utf8, size, 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))) { wcscpy_s(win_sys_path, _countof(win_sys_path), default_sys_path); win_sys_path[_countof(win_sys_path) - 1] = L'\0'; } return win_sys_path; }