OpenVPN
common.c
Go to the documentation of this file.
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) 2011-2018 Heiko Hund <heiko.hund@sophos.com>
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 along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #include "service.h"
25 #include "validate.h"
26 
27 LPCTSTR service_instance = TEXT("");
28 static wchar_t win_sys_path[MAX_PATH];
29 
30 /*
31  * These are necessary due to certain buggy implementations of (v)snprintf,
32  * that don't guarantee null termination for size > 0.
33  */
34 BOOL
35 openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
36 {
37  int len = -1;
38  if (size > 0)
39  {
40  len = _vsntprintf(str, size, format, arglist);
41  str[size - 1] = 0;
42  }
43  return (len >= 0 && (size_t)len < size);
44 }
45 
46 BOOL
47 openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format, ...)
48 {
49  va_list arglist;
50  BOOL res = FALSE;
51  if (size > 0)
52  {
53  va_start(arglist, format);
54  res = openvpn_vsntprintf(str, size, format, arglist);
55  va_end(arglist);
56  }
57  return res;
58 }
59 
60 BOOL
61 openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format, ...)
62 {
63  va_list arglist;
64  int len = -1;
65  if (size > 0)
66  {
67  va_start(arglist, format);
68  len = vswprintf(str, size, format, arglist);
69  va_end(arglist);
70  str[size - 1] = L'\0';
71  }
72  return (len >= 0 && len < size);
73 }
74 
75 static DWORD
76 GetRegString(HKEY key, LPCTSTR value, LPTSTR data, DWORD size, LPCTSTR default_value)
77 {
78  LONG status = RegGetValue(key, NULL, value, RRF_RT_REG_SZ,
79  NULL, (LPBYTE) data, &size);
80 
81  if (status == ERROR_FILE_NOT_FOUND && default_value)
82  {
83  size_t len = size/sizeof(data[0]);
84  if (openvpn_sntprintf(data, len, default_value))
85  {
86  status = ERROR_SUCCESS;
87  }
88  }
89 
90  if (status != ERROR_SUCCESS)
91  {
92  SetLastError(status);
93  return MsgToEventLog(M_SYSERR, TEXT("Error querying registry value: HKLM\\SOFTWARE\\" PACKAGE_NAME "%s\\%s"), service_instance, value);
94  }
95 
96  return ERROR_SUCCESS;
97 }
98 
99 
100 DWORD
102 {
103  TCHAR reg_path[256];
104  TCHAR priority[64];
105  TCHAR append[2];
106  DWORD error;
107  HKEY key;
108  TCHAR install_path[MAX_PATH];
109  TCHAR default_value[MAX_PATH];
110 
111  openvpn_sntprintf(reg_path, _countof(reg_path), TEXT("SOFTWARE\\" PACKAGE_NAME "%s"), service_instance);
112 
113  LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_path, 0, KEY_READ, &key);
114  if (status != ERROR_SUCCESS)
115  {
116  SetLastError(status);
117  return MsgToEventLog(M_SYSERR, TEXT("Could not open Registry key HKLM\\%s not found"), reg_path);
118  }
119 
120  /* The default value of REG_KEY is the install path */
121  if (GetRegString(key, NULL, install_path, sizeof(install_path), NULL) != ERROR_SUCCESS)
122  {
123  goto out;
124  }
125 
126  openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\bin\\openvpn.exe"),
127  install_path);
128  error = GetRegString(key, TEXT("exe_path"), s->exe_path, sizeof(s->exe_path), default_value);
129  if (error != ERROR_SUCCESS)
130  {
131  goto out;
132  }
133 
134  openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\config"), install_path);
135  error = GetRegString(key, TEXT("config_dir"), s->config_dir, sizeof(s->config_dir),
136  default_value);
137  if (error != ERROR_SUCCESS)
138  {
139  goto out;
140  }
141 
142  error = GetRegString(key, TEXT("config_ext"), s->ext_string, sizeof(s->ext_string),
143  TEXT(".ovpn"));
144  if (error != ERROR_SUCCESS)
145  {
146  goto out;
147  }
148 
149  openvpn_sntprintf(default_value, _countof(default_value), TEXT("%s\\log"), install_path);
150  error = GetRegString(key, TEXT("log_dir"), s->log_dir, sizeof(s->log_dir), default_value);
151  if (error != ERROR_SUCCESS)
152  {
153  goto out;
154  }
155 
156  error = GetRegString(key, TEXT("priority"), priority, sizeof(priority),
157  TEXT("NORMAL_PRIORITY_CLASS"));
158  if (error != ERROR_SUCCESS)
159  {
160  goto out;
161  }
162 
163  error = GetRegString(key, TEXT("log_append"), append, sizeof(append), TEXT("0"));
164  if (error != ERROR_SUCCESS)
165  {
166  goto out;
167  }
168 
169  /* read if present, else use default */
170  error = GetRegString(key, TEXT("ovpn_admin_group"), s->ovpn_admin_group,
171  sizeof(s->ovpn_admin_group), OVPN_ADMIN_GROUP);
172  if (error != ERROR_SUCCESS)
173  {
174  goto out;
175  }
176  /* set process priority */
177  if (!_tcsicmp(priority, TEXT("IDLE_PRIORITY_CLASS")))
178  {
179  s->priority = IDLE_PRIORITY_CLASS;
180  }
181  else if (!_tcsicmp(priority, TEXT("BELOW_NORMAL_PRIORITY_CLASS")))
182  {
183  s->priority = BELOW_NORMAL_PRIORITY_CLASS;
184  }
185  else if (!_tcsicmp(priority, TEXT("NORMAL_PRIORITY_CLASS")))
186  {
187  s->priority = NORMAL_PRIORITY_CLASS;
188  }
189  else if (!_tcsicmp(priority, TEXT("ABOVE_NORMAL_PRIORITY_CLASS")))
190  {
191  s->priority = ABOVE_NORMAL_PRIORITY_CLASS;
192  }
193  else if (!_tcsicmp(priority, TEXT("HIGH_PRIORITY_CLASS")))
194  {
195  s->priority = HIGH_PRIORITY_CLASS;
196  }
197  else
198  {
199  SetLastError(ERROR_INVALID_DATA);
200  error = MsgToEventLog(M_SYSERR, TEXT("Unknown priority name: %s"), priority);
201  goto out;
202  }
203 
204  /* set log file append/truncate flag */
205  if (append[0] == TEXT('0'))
206  {
207  s->append = FALSE;
208  }
209  else if (append[0] == TEXT('1'))
210  {
211  s->append = TRUE;
212  }
213  else
214  {
215  SetLastError(ERROR_INVALID_DATA);
216  error = MsgToEventLog(M_ERR, TEXT("Log file append flag (given as '%s') must be '0' or '1'"), append);
217  goto out;
218  }
219 
220 out:
221  RegCloseKey(key);
222  return error;
223 }
224 
225 
226 LPCTSTR
228 {
229  static TCHAR buf[256];
230  DWORD len;
231  LPTSTR tmp = NULL;
232 
233  len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
234  NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL);
235 
236  if (len == 0 || (long) _countof(buf) < (long) len + 14)
237  {
238  buf[0] = TEXT('\0');
239  }
240  else
241  {
242  tmp[_tcslen(tmp) - 2] = TEXT('\0'); /* remove CR/LF characters */
243  openvpn_sntprintf(buf, _countof(buf), TEXT("%s (0x%x)"), tmp, GetLastError());
244  }
245 
246  if (tmp)
247  {
248  LocalFree(tmp);
249  }
250 
251  return buf;
252 }
253 
254 
255 DWORD
256 MsgToEventLog(DWORD flags, LPCTSTR format, ...)
257 {
258  HANDLE hEventSource;
259  TCHAR msg[2][256];
260  DWORD error = 0;
261  LPCTSTR err_msg = TEXT("");
262  va_list arglist;
263 
264  if (flags & MSG_FLAGS_SYS_CODE)
265  {
266  error = GetLastError();
267  err_msg = GetLastErrorText();
268  }
269 
270  hEventSource = RegisterEventSource(NULL, APPNAME);
271  if (hEventSource != NULL)
272  {
273  openvpn_sntprintf(msg[0], _countof(msg[0]),
274  TEXT("%s%s%s: %s"), APPNAME, service_instance,
275  (flags & MSG_FLAGS_ERROR) ? TEXT(" error") : TEXT(""), err_msg);
276 
277  va_start(arglist, format);
278  openvpn_vsntprintf(msg[1], _countof(msg[1]), format, arglist);
279  va_end(arglist);
280 
281  const TCHAR *mesg[] = { msg[0], msg[1] };
282  ReportEvent(hEventSource, flags & MSG_FLAGS_ERROR ?
283  EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
284  0, 0, NULL, 2, 0, mesg, NULL);
285  DeregisterEventSource(hEventSource);
286  }
287 
288  return error;
289 }
290 
291 /* Convert a utf8 string to utf16. Caller should free the result */
292 wchar_t *
293 utf8to16(const char *utf8)
294 {
295  int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
296  wchar_t *utf16 = malloc(n * sizeof(wchar_t));
297  if (!utf16)
298  {
299  return NULL;
300  }
301  MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, n);
302  return utf16;
303 }
304 
305 const wchar_t *
307 {
308  const wchar_t *default_sys_path = L"C:\\Windows\\system32";
309 
310  if (!GetSystemDirectoryW(win_sys_path, _countof(win_sys_path)))
311  {
312  wcsncpy(win_sys_path, default_sys_path, _countof(win_sys_path));
313  win_sys_path[_countof(win_sys_path) - 1] = L'\0';
314  }
315 
316  return win_sys_path;
317 }
BOOL openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format,...)
Definition: common.c:47
DWORD MsgToEventLog(DWORD flags, LPCTSTR format,...)
Definition: common.c:256
LPCTSTR service_instance
Definition: common.c:27
TCHAR config_dir[MAX_PATH]
Definition: service.h:67
static DWORD GetRegString(HKEY key, LPCTSTR value, LPTSTR data, DWORD size, LPCTSTR default_value)
Definition: common.c:76
#define MSG_FLAGS_ERROR
Definition: service.h:44
TCHAR exe_path[MAX_PATH]
Definition: service.h:66
BOOL append
Definition: service.h:72
DWORD priority
Definition: service.h:71
TCHAR ext_string[16]
Definition: service.h:68
static wchar_t win_sys_path[MAX_PATH]
Definition: common.c:28
list flags
#define malloc
Definition: cmocka.c:1795
#define PACKAGE_NAME
Definition: config.h:730
#define OVPN_ADMIN_GROUP
Definition: validate.h:32
wchar_t * utf8to16(const char *utf8)
Definition: common.c:293
static SERVICE_STATUS status
Definition: automatic.c:43
DWORD GetOpenvpnSettings(settings_t *s)
Definition: common.c:101
#define msg
Definition: error.h:173
LPCTSTR GetLastErrorText()
Definition: common.c:227
BOOL openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
Definition: common.c:35
TCHAR ovpn_admin_group[MAX_NAME]
Definition: service.h:70
BOOL openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format,...)
Definition: common.c:61
#define M_ERR
Definition: error.h:110
#define M_SYSERR
Definition: service.h:47
TCHAR log_dir[MAX_PATH]
Definition: service.h:69
#define MSG_FLAGS_SYS_CODE
Definition: service.h:45
const wchar_t * get_win_sys_path(void)
Definition: common.c:306
#define APPNAME
Definition: service.h:38
Container for unidirectional cipher and HMAC key material.
Definition: crypto.h:151