~ubuntu-branches/ubuntu/saucy/openvpn/saucy-proposed

« back to all changes in this revision

Viewing changes to src/openvpnserv/openvpnserv.c

  • Committer: Package Import Robot
  • Author(s): Stéphane Graber
  • Date: 2013-05-24 17:42:45 UTC
  • mfrom: (1.1.19) (10.2.22 sid)
  • Revision ID: package-import@ubuntu.com-20130524174245-g9y6wlforycufqy5
Tags: 2.3.1-2ubuntu1
* Merge from Debian unstable. Remaining changes:
  - debian/openvpn.init.d:
    + Do not use start-stop-daemon and </dev/null to avoid blocking boot.
    + Show per-VPN result messages.
    + Add "--script-security 2" by default for backwards compatabliity.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
61
struct 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
 
 
82
static 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
 
 
137
int 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
 
 
152
bool
 
153
init_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
 
 
167
HANDLE
 
168
create_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
 
 
181
void
 
182
close_if_open (HANDLE h)
 
183
{
 
184
  if (h != NULL)
 
185
    CloseHandle (h);
 
186
}
 
187
 
 
188
static bool
 
189
match (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
 */
 
209
static bool
 
210
modext (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
 
 
240
VOID 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
 
 
530
VOID ServiceStop()
 
531
{
 
532
  if (exit_event)
 
533
    SetEvent(exit_event);
 
534
}