• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/openvpn/src/openvpnserv/
1/*
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
6 *             packet compression.
7 *
8 *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
9 *
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.
13 *
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.
18 *
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
23 */
24
25/*
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.
29 *
30 * You should also apply service.patch to
31 * service.c and service.h from the Platform SDK service sample.
32 *
33 * This code is designed to be built with the mingw compiler.
34 */
35
36#ifdef HAVE_CONFIG_H
37#include "config.h"
38#elif defined(_MSC_VER)
39#include "config-msvc.h"
40#endif
41#include <windows.h>
42#include <stdlib.h>
43#include <stdio.h>
44#include <stdarg.h>
45#include <process.h>
46#include "service.h"
47
48/* bool definitions */
49#define bool int
50#define true 1
51#define false 0
52
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
56#endif
57#ifndef ABOVE_NORMAL_PRIORITY_CLASS
58#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
59#endif
60
61struct security_attributes
62{
63  SECURITY_ATTRIBUTES sa;
64  SECURITY_DESCRIPTOR sd;
65};
66
67/*
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.
73 */
74#define EXIT_EVENT_NAME PACKAGE "_exit_1"
75
76/*
77 * Which registry key in HKLM should
78 * we get config info from?
79 */
80#define REG_KEY "SOFTWARE\\" PACKAGE_NAME
81
82static HANDLE exit_event = NULL;
83
84/* clear an object */
85#define CLEAR(x) memset(&(x), 0, sizeof(x))
86
87/*
88 * Message handling
89 */
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 */
93
94/* write error to event log */
95#define MSG(flags, ...) \
96        { \
97           char x_msg[256]; \
98           openvpn_snprintf (x_msg, sizeof(x_msg), __VA_ARGS__);      \
99           AddToMessageLog ((flags), x_msg); \
100        }
101
102/* get a registry string */
103#define QUERY_REG_STRING(name, data) \
104  { \
105    len = sizeof (data); \
106    status = RegQueryValueEx(openvpn_key, name, NULL, &type, data, &len); \
107    if (status != ERROR_SUCCESS || type != REG_SZ) \
108      { \
109        SetLastError (status); \
110        MSG (M_SYSERR, error_format_str, name); \
111	RegCloseKey (openvpn_key); \
112	goto finish; \
113      } \
114  }
115
116/* get a registry string */
117#define QUERY_REG_DWORD(name, data) \
118  { \
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)) \
122      { \
123        SetLastError (status); \
124        MSG (M_SYSERR, error_format_dword, name); \
125	RegCloseKey (openvpn_key); \
126	goto finish; \
127      } \
128  }
129
130/*
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)
135 */
136
137int openvpn_snprintf(char *str, size_t size, const char *format, ...)
138{
139  va_list arglist;
140  int len = -1;
141  if (size > 0)
142    {
143      va_start (arglist, format);
144      len = vsnprintf (str, size, format, arglist);
145      va_end (arglist);
146      str[size - 1] = 0;
147    }
148  return (len >= 0 && len < size);
149}
150
151
152bool
153init_security_attributes_allow_all (struct security_attributes *obj)
154{
155  CLEAR (*obj);
156
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))
161    return false;
162  if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE))
163    return false;
164  return true;
165}
166
167HANDLE
168create_event (const char *name, bool allow_all, bool initial_state, bool manual_reset)
169{
170  if (allow_all)
171    {
172      struct security_attributes sa;
173      if (!init_security_attributes_allow_all (&sa))
174	return NULL;
175      return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name);
176    }
177  else
178    return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name);
179}
180
181void
182close_if_open (HANDLE h)
183{
184  if (h != NULL)
185    CloseHandle (h);
186}
187
188static bool
189match (const WIN32_FIND_DATA *find, const char *ext)
190{
191  int i;
192
193  if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
194    return false;
195
196  if (!strlen (ext))
197    return true;
198
199  i = strlen (find->cFileName) - strlen (ext) - 1;
200  if (i < 1)
201    return false;
202
203  return find->cFileName[i] == '.' && !_stricmp (find->cFileName + i + 1, ext);
204}
205
206/*
207 * Modify the extension on a filename.
208 */
209static bool
210modext (char *dest, int size, const char *src, const char *newext)
211{
212  int i;
213
214  if (size > 0 && (strlen (src) + 1) <= size)
215    {
216      strcpy (dest, src);
217      dest [size - 1] = '\0';
218      i = strlen (dest);
219      while (--i >= 0)
220	{
221	  if (dest[i] == '\\')
222	    break;
223	  if (dest[i] == '.')
224	    {
225	      dest[i] = '\0';
226	      break;
227	    }
228	}
229      if (strlen (dest) + strlen(newext) + 2 <= size)
230	{
231	  strcat (dest, ".");
232	  strcat (dest, newext);
233	  return true;
234	}
235      dest [0] = '\0';
236    }
237  return false;
238}
239
240VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
241{
242  char exe_path[MAX_PATH];
243  char config_dir[MAX_PATH];
244  char ext_string[16];
245  char log_dir[MAX_PATH];
246  char priority_string[64];
247  char append_string[2];
248
249  DWORD priority;
250  bool append;
251
252  ResetError ();
253
254  if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
255    {
256      MSG (M_ERR, "ReportStatusToSCMgr #1 failed");
257      goto finish;
258    }
259
260  /*
261   * Create our exit event
262   */
263  exit_event = create_event (EXIT_EVENT_NAME, false, false, true);
264  if (!exit_event)
265    {
266      MSG (M_ERR, "CreateEvent failed");
267      goto finish;
268    }
269
270  /*
271   * If exit event is already signaled, it means we were not
272   * shut down properly.
273   */
274  if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT)
275    {
276      MSG (M_ERR, "Exit event is already signaled -- we were not shut down properly");
277      goto finish;
278    }
279
280  if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
281    {
282      MSG (M_ERR, "ReportStatusToSCMgr #2 failed");
283      goto finish;
284    }
285
286  /*
287   * Read info from registry in key HKLM\SOFTWARE\OpenVPN
288   */
289  {
290    HKEY openvpn_key;
291    LONG status;
292    DWORD len;
293    DWORD type;
294
295    static const char error_format_str[] =
296      "Error querying registry key of type REG_SZ: HKLM\\" REG_KEY "\\%s";
297
298    static const char error_format_dword[] =
299      "Error querying registry key of type REG_DWORD: HKLM\\" REG_KEY "\\%s";
300
301    status = RegOpenKeyEx(
302			  HKEY_LOCAL_MACHINE,
303			  REG_KEY,
304			  0,
305			  KEY_READ,
306			  &openvpn_key);
307
308    if (status != ERROR_SUCCESS)
309      {
310	SetLastError (status);
311	MSG (M_SYSERR, "Registry key HKLM\\" REG_KEY " not found");
312	goto finish;
313      }
314
315    /* get path to openvpn.exe */
316    QUERY_REG_STRING ("exe_path", exe_path);
317
318    /* get path to configuration directory */
319    QUERY_REG_STRING ("config_dir", config_dir);
320
321    /* get extension on configuration files */
322    QUERY_REG_STRING ("config_ext", ext_string);
323
324    /* get path to log directory */
325    QUERY_REG_STRING ("log_dir", log_dir);
326
327    /* get priority for spawned OpenVPN subprocesses */
328    QUERY_REG_STRING ("priority", priority_string);
329
330    /* should we truncate or append to logfile? */
331    QUERY_REG_STRING ("log_append", append_string);
332
333    RegCloseKey (openvpn_key);
334  }
335
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;
348  else
349    {
350      MSG (M_ERR, "Unknown priority name: %s", priority_string);
351      goto finish;
352    }
353
354  /* set log file append/truncate flag */
355  append = false;
356  if (append_string[0] == '0')
357    append = false;
358  else if (append_string[0] == '1')
359    append = true;
360  else
361    {
362      MSG (M_ERR, "Log file append flag (given as '%s') must be '0' or '1'", append_string);
363      goto finish;
364    }
365
366  /*
367   * Instantiate an OpenVPN process for each configuration
368   * file found.
369   */
370  {
371    WIN32_FIND_DATA find_obj;
372    HANDLE find_handle;
373    BOOL more_files;
374    char find_string[MAX_PATH];
375
376    openvpn_snprintf (find_string, MAX_PATH, "%s\\*", config_dir);
377
378    find_handle = FindFirstFile (find_string, &find_obj);
379    if (find_handle == INVALID_HANDLE_VALUE)
380      {
381        MSG (M_ERR, "Cannot get configuration file list using: %s", find_string);
382	goto finish;
383      }
384
385    /*
386     * Loop over each config file
387     */
388    do {
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];
396
397      CLEAR (start_info);
398      CLEAR (proc_info);
399      CLEAR (sa);
400
401      if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
402	{
403	  MSG (M_ERR, "ReportStatusToSCMgr #3 failed");
404	  FindClose (find_handle);
405	  goto finish;
406	}
407
408      /* does file have the correct type and extension? */
409      if (match (&find_obj, ext_string))
410	{
411	  /* get log file pathname */
412	  if (!modext (log_file, sizeof (log_file), find_obj.cFileName, "log"))
413	    {
414	      MSG (M_ERR, "Cannot construct logfile name based on: %s", find_obj.cFileName);
415	      FindClose (find_handle);
416	      goto finish;
417	    }
418	  openvpn_snprintf (log_path, sizeof(log_path),
419                            "%s\\%s", log_dir, log_file);
420
421	  /* construct command line */
422	  openvpn_snprintf (command_line, sizeof(command_line), PACKAGE " --service %s 1 --config \"%s\"",
423		      EXIT_EVENT_NAME,
424		      find_obj.cFileName);
425
426	  /* Make security attributes struct for logfile handle so it can
427	     be inherited. */
428	  if (!init_security_attributes_allow_all (&sa))
429	    {
430	      MSG (M_SYSERR, "InitializeSecurityDescriptor start_" PACKAGE " failed");
431	      goto finish;
432	    }
433
434	  /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */
435	  log_handle = CreateFile (log_path,
436				   GENERIC_WRITE,
437				   FILE_SHARE_READ,
438				   &sa.sa,
439				   append ? OPEN_ALWAYS : CREATE_ALWAYS,
440				   FILE_ATTRIBUTE_NORMAL,
441				   NULL);
442
443	  if (log_handle == INVALID_HANDLE_VALUE)
444	    {
445	      MSG (M_SYSERR, "Cannot open logfile: %s", log_path);
446	      FindClose (find_handle);
447	      goto finish;
448	    }
449
450	  /* append to logfile? */
451	  if (append)
452	    {
453	      if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
454		{
455		  MSG (M_SYSERR, "Cannot seek to end of logfile: %s", log_path);
456		  FindClose (find_handle);
457		  goto finish;
458		}
459	    }
460
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;
468
469	  /* create an OpenVPN process for one config file */
470	  if (!CreateProcess(exe_path,
471			     command_line,
472			     NULL,
473			     NULL,
474			     TRUE,
475			     priority | CREATE_NEW_CONSOLE,
476			     NULL,
477			     config_dir,
478			     &start_info,
479			     &proc_info))
480	    {
481	      MSG (M_SYSERR, "CreateProcess failed, exe='%s' cmdline='%s' dir='%s'",
482		   exe_path,
483		   command_line,
484		   config_dir);
485
486	      FindClose (find_handle);
487	      CloseHandle (log_handle);
488	      goto finish;
489	    }
490
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))
497	    {
498	      MSG (M_SYSERR, "CloseHandle failed");
499	      goto finish;
500	    }
501	}
502
503      /* more files to process? */
504      more_files = FindNextFile (find_handle, &find_obj);
505
506    } while (more_files);
507
508    FindClose (find_handle);
509  }
510
511  /* we are now fully started */
512  if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
513    {
514      MSG (M_ERR, "ReportStatusToSCMgr SERVICE_RUNNING failed");
515      goto finish;
516    }
517
518  /* wait for our shutdown signal */
519  if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0)
520    {
521      MSG (M_ERR, "wait for shutdown signal failed");
522    }
523
524 finish:
525  ServiceStop ();
526  if (exit_event)
527    CloseHandle (exit_event);
528}
529
530VOID ServiceStop()
531{
532  if (exit_event)
533    SetEvent(exit_event);
534}
535