2
* OpenVPN -- An application to securely tunnel IP networks
3
* over a single TCP/UDP port, with support for SSL/TLS-based
4
* session authentication and key exchange,
5
* packet encryption, packet authentication, and
8
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
10
* This program is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU General Public License version 2
12
* as published by the Free Software Foundation.
14
* This program is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program (see the file COPYING included with this
21
* distribution); if not, write to the Free Software Foundation, Inc.,
22
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26
* This program allows one or more OpenVPN processes to be started
27
* as a service. To build, you must get the service sample from the
28
* Platform SDK and replace Simple.c with this file.
30
* You should also apply service.patch to
31
* service.c and service.h from the Platform SDK service sample.
33
* This code is designed to be built with the mingw compiler.
38
#elif defined(_MSC_VER)
39
#include "config-msvc.h"
48
/* bool definitions */
53
/* These are new for 2000/XP, so they aren't in the mingw headers yet */
54
#ifndef BELOW_NORMAL_PRIORITY_CLASS
55
#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000
57
#ifndef ABOVE_NORMAL_PRIORITY_CLASS
58
#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
61
struct security_attributes
63
SECURITY_ATTRIBUTES sa;
64
SECURITY_DESCRIPTOR sd;
68
* This event is initially created in the non-signaled
69
* state. It will transition to the signaled state when
70
* we have received a terminate signal from the Service
71
* Control Manager which will cause an asynchronous call
72
* of ServiceStop below.
74
#define EXIT_EVENT_NAME PACKAGE "_exit_1"
77
* Which registry key in HKLM should
78
* we get config info from?
80
#define REG_KEY "SOFTWARE\\" PACKAGE_NAME
82
static HANDLE exit_event = NULL;
85
#define CLEAR(x) memset(&(x), 0, sizeof(x))
90
#define M_INFO (0) /* informational */
91
#define M_SYSERR (MSG_FLAGS_ERROR|MSG_FLAGS_SYS_CODE) /* error + system code */
92
#define M_ERR (MSG_FLAGS_ERROR) /* error */
94
/* write error to event log */
95
#define MSG(flags, ...) \
98
openvpn_snprintf (x_msg, sizeof(x_msg), __VA_ARGS__); \
99
AddToMessageLog ((flags), x_msg); \
102
/* get a registry string */
103
#define QUERY_REG_STRING(name, data) \
105
len = sizeof (data); \
106
status = RegQueryValueEx(openvpn_key, name, NULL, &type, data, &len); \
107
if (status != ERROR_SUCCESS || type != REG_SZ) \
109
SetLastError (status); \
110
MSG (M_SYSERR, error_format_str, name); \
111
RegCloseKey (openvpn_key); \
116
/* get a registry string */
117
#define QUERY_REG_DWORD(name, data) \
119
len = sizeof (DWORD); \
120
status = RegQueryValueEx(openvpn_key, name, NULL, &type, (LPBYTE)&data, &len); \
121
if (status != ERROR_SUCCESS || type != REG_DWORD || len != sizeof (DWORD)) \
123
SetLastError (status); \
124
MSG (M_SYSERR, error_format_dword, name); \
125
RegCloseKey (openvpn_key); \
131
* This is necessary due to certain buggy implementations of snprintf,
132
* that don't guarantee null termination for size > 0.
133
* (copied from ../buffer.c, line 217)
134
* (git: 100644 blob e2f8caab0a5b2a870092c6cd508a1a50c21c3ba3 buffer.c)
137
int openvpn_snprintf(char *str, size_t size, const char *format, ...)
143
va_start (arglist, format);
144
len = vsnprintf (str, size, format, arglist);
148
return (len >= 0 && len < size);
153
init_security_attributes_allow_all (struct security_attributes *obj)
157
obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES);
158
obj->sa.lpSecurityDescriptor = &obj->sd;
159
obj->sa.bInheritHandle = TRUE;
160
if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION))
162
if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE))
168
create_event (const char *name, bool allow_all, bool initial_state, bool manual_reset)
172
struct security_attributes sa;
173
if (!init_security_attributes_allow_all (&sa))
175
return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name);
178
return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name);
182
close_if_open (HANDLE h)
189
match (const WIN32_FIND_DATA *find, const char *ext)
193
if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
199
i = strlen (find->cFileName) - strlen (ext) - 1;
203
return find->cFileName[i] == '.' && !_stricmp (find->cFileName + i + 1, ext);
207
* Modify the extension on a filename.
210
modext (char *dest, int size, const char *src, const char *newext)
214
if (size > 0 && (strlen (src) + 1) <= size)
217
dest [size - 1] = '\0';
229
if (strlen (dest) + strlen(newext) + 2 <= size)
232
strcat (dest, newext);
240
VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
242
char exe_path[MAX_PATH];
243
char config_dir[MAX_PATH];
245
char log_dir[MAX_PATH];
246
char priority_string[64];
247
char append_string[2];
254
if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
256
MSG (M_ERR, "ReportStatusToSCMgr #1 failed");
261
* Create our exit event
263
exit_event = create_event (EXIT_EVENT_NAME, false, false, true);
266
MSG (M_ERR, "CreateEvent failed");
271
* If exit event is already signaled, it means we were not
272
* shut down properly.
274
if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT)
276
MSG (M_ERR, "Exit event is already signaled -- we were not shut down properly");
280
if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
282
MSG (M_ERR, "ReportStatusToSCMgr #2 failed");
287
* Read info from registry in key HKLM\SOFTWARE\OpenVPN
295
static const char error_format_str[] =
296
"Error querying registry key of type REG_SZ: HKLM\\" REG_KEY "\\%s";
298
static const char error_format_dword[] =
299
"Error querying registry key of type REG_DWORD: HKLM\\" REG_KEY "\\%s";
301
status = RegOpenKeyEx(
308
if (status != ERROR_SUCCESS)
310
SetLastError (status);
311
MSG (M_SYSERR, "Registry key HKLM\\" REG_KEY " not found");
315
/* get path to openvpn.exe */
316
QUERY_REG_STRING ("exe_path", exe_path);
318
/* get path to configuration directory */
319
QUERY_REG_STRING ("config_dir", config_dir);
321
/* get extension on configuration files */
322
QUERY_REG_STRING ("config_ext", ext_string);
324
/* get path to log directory */
325
QUERY_REG_STRING ("log_dir", log_dir);
327
/* get priority for spawned OpenVPN subprocesses */
328
QUERY_REG_STRING ("priority", priority_string);
330
/* should we truncate or append to logfile? */
331
QUERY_REG_STRING ("log_append", append_string);
333
RegCloseKey (openvpn_key);
336
/* set process priority */
337
priority = NORMAL_PRIORITY_CLASS;
338
if (!_stricmp (priority_string, "IDLE_PRIORITY_CLASS"))
339
priority = IDLE_PRIORITY_CLASS;
340
else if (!_stricmp (priority_string, "BELOW_NORMAL_PRIORITY_CLASS"))
341
priority = BELOW_NORMAL_PRIORITY_CLASS;
342
else if (!_stricmp (priority_string, "NORMAL_PRIORITY_CLASS"))
343
priority = NORMAL_PRIORITY_CLASS;
344
else if (!_stricmp (priority_string, "ABOVE_NORMAL_PRIORITY_CLASS"))
345
priority = ABOVE_NORMAL_PRIORITY_CLASS;
346
else if (!_stricmp (priority_string, "HIGH_PRIORITY_CLASS"))
347
priority = HIGH_PRIORITY_CLASS;
350
MSG (M_ERR, "Unknown priority name: %s", priority_string);
354
/* set log file append/truncate flag */
356
if (append_string[0] == '0')
358
else if (append_string[0] == '1')
362
MSG (M_ERR, "Log file append flag (given as '%s') must be '0' or '1'", append_string);
367
* Instantiate an OpenVPN process for each configuration
371
WIN32_FIND_DATA find_obj;
374
char find_string[MAX_PATH];
376
openvpn_snprintf (find_string, MAX_PATH, "%s\\*", config_dir);
378
find_handle = FindFirstFile (find_string, &find_obj);
379
if (find_handle == INVALID_HANDLE_VALUE)
381
MSG (M_ERR, "Cannot get configuration file list using: %s", find_string);
386
* Loop over each config file
389
HANDLE log_handle = NULL;
390
STARTUPINFO start_info;
391
PROCESS_INFORMATION proc_info;
392
struct security_attributes sa;
393
char log_file[MAX_PATH];
394
char log_path[MAX_PATH];
395
char command_line[256];
401
if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
403
MSG (M_ERR, "ReportStatusToSCMgr #3 failed");
404
FindClose (find_handle);
408
/* does file have the correct type and extension? */
409
if (match (&find_obj, ext_string))
411
/* get log file pathname */
412
if (!modext (log_file, sizeof (log_file), find_obj.cFileName, "log"))
414
MSG (M_ERR, "Cannot construct logfile name based on: %s", find_obj.cFileName);
415
FindClose (find_handle);
418
openvpn_snprintf (log_path, sizeof(log_path),
419
"%s\\%s", log_dir, log_file);
421
/* construct command line */
422
openvpn_snprintf (command_line, sizeof(command_line), PACKAGE " --service %s 1 --config \"%s\"",
426
/* Make security attributes struct for logfile handle so it can
428
if (!init_security_attributes_allow_all (&sa))
430
MSG (M_SYSERR, "InitializeSecurityDescriptor start_" PACKAGE " failed");
434
/* open logfile as stdout/stderr for soon-to-be-spawned subprocess */
435
log_handle = CreateFile (log_path,
439
append ? OPEN_ALWAYS : CREATE_ALWAYS,
440
FILE_ATTRIBUTE_NORMAL,
443
if (log_handle == INVALID_HANDLE_VALUE)
445
MSG (M_SYSERR, "Cannot open logfile: %s", log_path);
446
FindClose (find_handle);
450
/* append to logfile? */
453
if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
455
MSG (M_SYSERR, "Cannot seek to end of logfile: %s", log_path);
456
FindClose (find_handle);
461
/* fill in STARTUPINFO struct */
462
GetStartupInfo(&start_info);
463
start_info.cb = sizeof(start_info);
464
start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
465
start_info.wShowWindow = SW_HIDE;
466
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
467
start_info.hStdOutput = start_info.hStdError = log_handle;
469
/* create an OpenVPN process for one config file */
470
if (!CreateProcess(exe_path,
475
priority | CREATE_NEW_CONSOLE,
481
MSG (M_SYSERR, "CreateProcess failed, exe='%s' cmdline='%s' dir='%s'",
486
FindClose (find_handle);
487
CloseHandle (log_handle);
491
/* close unneeded handles */
492
Sleep (1000); /* try to prevent race if we close logfile
493
handle before child process DUPs it */
494
if (!CloseHandle (proc_info.hProcess)
495
|| !CloseHandle (proc_info.hThread)
496
|| !CloseHandle (log_handle))
498
MSG (M_SYSERR, "CloseHandle failed");
503
/* more files to process? */
504
more_files = FindNextFile (find_handle, &find_obj);
506
} while (more_files);
508
FindClose (find_handle);
511
/* we are now fully started */
512
if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
514
MSG (M_ERR, "ReportStatusToSCMgr SERVICE_RUNNING failed");
518
/* wait for our shutdown signal */
519
if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0)
521
MSG (M_ERR, "wait for shutdown signal failed");
527
CloseHandle (exit_event);
533
SetEvent(exit_event);