diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-11-21 09:37:33 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-11-21 09:37:33 +0100 |
commit | 93b77cacdbb7e6f310c4e20f85c3a24ed5ba18ba (patch) | |
tree | 55a7688c9969ef4d01625caa58c7f679098c76eb /src/openvpnserv/automatic.c | |
parent | daa9ef0efeb5e10a1b43820fbab3a4ff5fbd22f1 (diff) | |
parent | 20c8675ba46bda97330a4117c459a59a9f1c465e (diff) |
Merge tag 'upstream/2.4_beta1'
Upstream version 2.4~beta1
Diffstat (limited to 'src/openvpnserv/automatic.c')
-rw-r--r-- | src/openvpnserv/automatic.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/src/openvpnserv/automatic.c b/src/openvpnserv/automatic.c new file mode 100644 index 0000000..aa7618f --- /dev/null +++ b/src/openvpnserv/automatic.c @@ -0,0 +1,415 @@ +/* + * 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) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * + * 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 (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 + */ + +/* + * This program allows one or more OpenVPN processes to be started + * as a service. To build, you must get the service sample from the + * Platform SDK and replace Simple.c with this file. + * + * You should also apply service.patch to + * service.c and service.h from the Platform SDK service sample. + * + * This code is designed to be built with the mingw compiler. + */ + +#include "service.h" + +#include <stdio.h> +#include <stdarg.h> +#include <process.h> + +/* bool definitions */ +#define bool int +#define true 1 +#define false 0 + +static SERVICE_STATUS_HANDLE service; +static SERVICE_STATUS status; + +openvpn_service_t automatic_service = { + automatic, + TEXT(PACKAGE_NAME "ServiceLegacy"), + TEXT(PACKAGE_NAME " Legacy Service"), + TEXT(SERVICE_DEPENDENCIES), + SERVICE_DEMAND_START +}; + +struct security_attributes +{ + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; +}; + +/* + * Which registry key in HKLM should + * we get config info from? + */ +#define REG_KEY "SOFTWARE\\" PACKAGE_NAME + +static HANDLE exit_event = NULL; + +/* clear an object */ +#define CLEAR(x) memset(&(x), 0, sizeof(x)) + + +bool +init_security_attributes_allow_all (struct security_attributes *obj) +{ + CLEAR (*obj); + + obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES); + obj->sa.lpSecurityDescriptor = &obj->sd; + obj->sa.bInheritHandle = TRUE; + if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION)) + return false; + if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE)) + return false; + return true; +} + +/* + * This event is initially created in the non-signaled + * state. It will transition to the signaled state when + * we have received a terminate signal from the Service + * Control Manager which will cause an asynchronous call + * of ServiceStop below. + */ +#define EXIT_EVENT_NAME TEXT(PACKAGE "_exit_1") + +HANDLE +create_event (LPCTSTR name, bool allow_all, bool initial_state, bool manual_reset) +{ + if (allow_all) + { + struct security_attributes sa; + if (!init_security_attributes_allow_all (&sa)) + return NULL; + return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name); + } + else + return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name); +} + +void +close_if_open (HANDLE h) +{ + if (h != NULL) + CloseHandle (h); +} + +static bool +match (const WIN32_FIND_DATA *find, LPCTSTR ext) +{ + int i; + + if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return false; + + if (!_tcslen (ext)) + return true; + + i = _tcslen (find->cFileName) - _tcslen (ext) - 1; + if (i < 1) + return false; + + return find->cFileName[i] == '.' && !_tcsicmp (find->cFileName + i + 1, ext); +} + +/* + * Modify the extension on a filename. + */ +static bool +modext (LPTSTR dest, int size, LPCTSTR src, LPCTSTR newext) +{ + int i; + + if (size > 0 && (_tcslen (src) + 1) <= size) + { + _tcscpy (dest, src); + dest [size - 1] = TEXT('\0'); + i = _tcslen (dest); + while (--i >= 0) + { + if (dest[i] == TEXT('\\')) + break; + if (dest[i] == TEXT('.')) + { + dest[i] = TEXT('\0'); + break; + } + } + if (_tcslen (dest) + _tcslen(newext) + 2 <= size) + { + _tcscat (dest, TEXT(".")); + _tcscat (dest, newext); + return true; + } + dest[0] = TEXT('\0'); + } + return false; +} + +static DWORD WINAPI +ServiceCtrlAutomatic (DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx) +{ + SERVICE_STATUS *status = ctx; + switch (ctrl_code) + { + case SERVICE_CONTROL_STOP: + status->dwCurrentState = SERVICE_STOP_PENDING; + ReportStatusToSCMgr (service, status); + if (exit_event) + SetEvent (exit_event); + return NO_ERROR; + + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + + default: + return ERROR_CALL_NOT_IMPLEMENTED; + } +} + + +VOID WINAPI +ServiceStartAutomatic (DWORD dwArgc, LPTSTR *lpszArgv) +{ + DWORD error = NO_ERROR; + settings_t settings; + + service = RegisterServiceCtrlHandlerEx (automatic_service.name, ServiceCtrlAutomatic, &status); + if (!service) + return; + + status.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; + status.dwCurrentState = SERVICE_START_PENDING; + status.dwServiceSpecificExitCode = NO_ERROR; + status.dwWin32ExitCode = NO_ERROR; + status.dwWaitHint = 3000; + + if (!ReportStatusToSCMgr(service, &status)) + { + MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #1 failed")); + goto finish; + } + + /* + * Create our exit event + */ + exit_event = create_event (EXIT_EVENT_NAME, false, false, true); + if (!exit_event) + { + MsgToEventLog (M_ERR, TEXT("CreateEvent failed")); + goto finish; + } + + /* + * If exit event is already signaled, it means we were not + * shut down properly. + */ + if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT) + { + MsgToEventLog (M_ERR, TEXT("Exit event is already signaled -- we were not shut down properly")); + goto finish; + } + + if (!ReportStatusToSCMgr(service, &status)) + { + MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #2 failed")); + goto finish; + } + + /* + * Read info from registry in key HKLM\SOFTWARE\OpenVPN + */ + error = GetOpenvpnSettings (&settings); + if (error != ERROR_SUCCESS) + goto finish; + + /* + * Instantiate an OpenVPN process for each configuration + * file found. + */ + { + WIN32_FIND_DATA find_obj; + HANDLE find_handle; + BOOL more_files; + TCHAR find_string[MAX_PATH]; + + openvpn_sntprintf (find_string, MAX_PATH, TEXT("%s\\*"), settings.config_dir); + + find_handle = FindFirstFile (find_string, &find_obj); + if (find_handle == INVALID_HANDLE_VALUE) + { + MsgToEventLog (M_ERR, TEXT("Cannot get configuration file list using: %s"), find_string); + goto finish; + } + + /* + * Loop over each config file + */ + do { + HANDLE log_handle = NULL; + STARTUPINFO start_info; + PROCESS_INFORMATION proc_info; + struct security_attributes sa; + TCHAR log_file[MAX_PATH]; + TCHAR log_path[MAX_PATH]; + TCHAR command_line[256]; + + CLEAR (start_info); + CLEAR (proc_info); + CLEAR (sa); + + if (!ReportStatusToSCMgr(service, &status)) + { + MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #3 failed")); + FindClose (find_handle); + goto finish; + } + + /* does file have the correct type and extension? */ + if (match (&find_obj, settings.ext_string)) + { + /* get log file pathname */ + if (!modext (log_file, _countof (log_file), find_obj.cFileName, TEXT("log"))) + { + MsgToEventLog (M_ERR, TEXT("Cannot construct logfile name based on: %s"), find_obj.cFileName); + FindClose (find_handle); + goto finish; + } + openvpn_sntprintf (log_path, _countof (log_path), + TEXT("%s\\%s"), settings.log_dir, log_file); + + /* construct command line */ + openvpn_sntprintf (command_line, _countof (command_line), TEXT(PACKAGE " --service %s 1 --config \"%s\""), + EXIT_EVENT_NAME, + find_obj.cFileName); + + /* Make security attributes struct for logfile handle so it can + be inherited. */ + if (!init_security_attributes_allow_all (&sa)) + { + error = MsgToEventLog (M_SYSERR, TEXT("InitializeSecurityDescriptor start_" PACKAGE " failed")); + goto finish; + } + + /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */ + log_handle = CreateFile (log_path, + GENERIC_WRITE, + FILE_SHARE_READ, + &sa.sa, + settings.append ? OPEN_ALWAYS : CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (log_handle == INVALID_HANDLE_VALUE) + { + error = MsgToEventLog (M_SYSERR, TEXT("Cannot open logfile: %s"), log_path); + FindClose (find_handle); + goto finish; + } + + /* append to logfile? */ + if (settings.append) + { + if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) + { + error = MsgToEventLog (M_SYSERR, TEXT("Cannot seek to end of logfile: %s"), log_path); + FindClose (find_handle); + goto finish; + } + } + + /* fill in STARTUPINFO struct */ + GetStartupInfo(&start_info); + start_info.cb = sizeof(start_info); + start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + start_info.wShowWindow = SW_HIDE; + start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start_info.hStdOutput = start_info.hStdError = log_handle; + + /* create an OpenVPN process for one config file */ + if (!CreateProcess(settings.exe_path, + command_line, + NULL, + NULL, + TRUE, + settings.priority | CREATE_NEW_CONSOLE, + NULL, + settings.config_dir, + &start_info, + &proc_info)) + { + error = MsgToEventLog (M_SYSERR, TEXT("CreateProcess failed, exe='%s' cmdline='%s' dir='%s'"), + settings.exe_path, + command_line, + settings.config_dir); + + FindClose (find_handle); + CloseHandle (log_handle); + goto finish; + } + + /* close unneeded handles */ + Sleep (1000); /* try to prevent race if we close logfile + handle before child process DUPs it */ + if (!CloseHandle (proc_info.hProcess) + || !CloseHandle (proc_info.hThread) + || !CloseHandle (log_handle)) + { + error = MsgToEventLog (M_SYSERR, TEXT("CloseHandle failed")); + goto finish; + } + } + + /* more files to process? */ + more_files = FindNextFile (find_handle, &find_obj); + + } while (more_files); + + FindClose (find_handle); + } + + /* we are now fully started */ + status.dwCurrentState = SERVICE_RUNNING; + status.dwWaitHint = 0; + if (!ReportStatusToSCMgr(service, &status)) + { + MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr SERVICE_RUNNING failed")); + goto finish; + } + + /* wait for our shutdown signal */ + if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0) + MsgToEventLog (M_ERR, TEXT("wait for shutdown signal failed")); + +finish: + if (exit_event) + CloseHandle (exit_event); + + status.dwCurrentState = SERVICE_STOPPED; + status.dwWin32ExitCode = error; + ReportStatusToSCMgr (service, &status); +} |