PageRenderTime 5779ms CodeModel.GetById 21ms RepoModel.GetById 2ms app.codeStats 0ms

/release/src/router/openvpn/src/openvpnserv/openvpnserv.c

https://gitlab.com/envieidoc/advancedtomato2
C | 534 lines | 419 code | 40 blank | 75 comment | 35 complexity | a710c654c70ee71ffaa92e96d3c74ef7 MD5 | raw 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) 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. * This program allows one or more OpenVPN processes to be started
  26. * as a service. To build, you must get the service sample from the
  27. * Platform SDK and replace Simple.c with this file.
  28. *
  29. * You should also apply service.patch to
  30. * service.c and service.h from the Platform SDK service sample.
  31. *
  32. * This code is designed to be built with the mingw compiler.
  33. */
  34. #ifdef HAVE_CONFIG_H
  35. #include "config.h"
  36. #elif defined(_MSC_VER)
  37. #include "config-msvc.h"
  38. #endif
  39. #include <windows.h>
  40. #include <stdlib.h>
  41. #include <stdio.h>
  42. #include <stdarg.h>
  43. #include <process.h>
  44. #include "service.h"
  45. /* bool definitions */
  46. #define bool int
  47. #define true 1
  48. #define false 0
  49. /* These are new for 2000/XP, so they aren't in the mingw headers yet */
  50. #ifndef BELOW_NORMAL_PRIORITY_CLASS
  51. #define BELOW_NORMAL_PRIORITY_CLASS 0x00004000
  52. #endif
  53. #ifndef ABOVE_NORMAL_PRIORITY_CLASS
  54. #define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
  55. #endif
  56. struct security_attributes
  57. {
  58. SECURITY_ATTRIBUTES sa;
  59. SECURITY_DESCRIPTOR sd;
  60. };
  61. /*
  62. * This event is initially created in the non-signaled
  63. * state. It will transition to the signaled state when
  64. * we have received a terminate signal from the Service
  65. * Control Manager which will cause an asynchronous call
  66. * of ServiceStop below.
  67. */
  68. #define EXIT_EVENT_NAME PACKAGE "_exit_1"
  69. /*
  70. * Which registry key in HKLM should
  71. * we get config info from?
  72. */
  73. #define REG_KEY "SOFTWARE\\" PACKAGE_NAME
  74. static HANDLE exit_event = NULL;
  75. /* clear an object */
  76. #define CLEAR(x) memset(&(x), 0, sizeof(x))
  77. /*
  78. * Message handling
  79. */
  80. #define M_INFO (0) /* informational */
  81. #define M_SYSERR (MSG_FLAGS_ERROR|MSG_FLAGS_SYS_CODE) /* error + system code */
  82. #define M_ERR (MSG_FLAGS_ERROR) /* error */
  83. /* write error to event log */
  84. #define MSG(flags, ...) \
  85. { \
  86. char x_msg[256]; \
  87. openvpn_snprintf (x_msg, sizeof(x_msg), __VA_ARGS__); \
  88. AddToMessageLog ((flags), x_msg); \
  89. }
  90. /* get a registry string */
  91. #define QUERY_REG_STRING(name, data) \
  92. { \
  93. len = sizeof (data); \
  94. status = RegQueryValueEx(openvpn_key, name, NULL, &type, data, &len); \
  95. if (status != ERROR_SUCCESS || type != REG_SZ) \
  96. { \
  97. SetLastError (status); \
  98. MSG (M_SYSERR, error_format_str, name); \
  99. RegCloseKey (openvpn_key); \
  100. goto finish; \
  101. } \
  102. }
  103. /* get a registry string */
  104. #define QUERY_REG_DWORD(name, data) \
  105. { \
  106. len = sizeof (DWORD); \
  107. status = RegQueryValueEx(openvpn_key, name, NULL, &type, (LPBYTE)&data, &len); \
  108. if (status != ERROR_SUCCESS || type != REG_DWORD || len != sizeof (DWORD)) \
  109. { \
  110. SetLastError (status); \
  111. MSG (M_SYSERR, error_format_dword, name); \
  112. RegCloseKey (openvpn_key); \
  113. goto finish; \
  114. } \
  115. }
  116. /*
  117. * This is necessary due to certain buggy implementations of snprintf,
  118. * that don't guarantee null termination for size > 0.
  119. * (copied from ../buffer.c, line 217)
  120. * (git: 100644 blob e2f8caab0a5b2a870092c6cd508a1a50c21c3ba3 buffer.c)
  121. */
  122. int openvpn_snprintf(char *str, size_t size, const char *format, ...)
  123. {
  124. va_list arglist;
  125. int len = -1;
  126. if (size > 0)
  127. {
  128. va_start (arglist, format);
  129. len = vsnprintf (str, size, format, arglist);
  130. va_end (arglist);
  131. str[size - 1] = 0;
  132. }
  133. return (len >= 0 && len < size);
  134. }
  135. bool
  136. init_security_attributes_allow_all (struct security_attributes *obj)
  137. {
  138. CLEAR (*obj);
  139. obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES);
  140. obj->sa.lpSecurityDescriptor = &obj->sd;
  141. obj->sa.bInheritHandle = TRUE;
  142. if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION))
  143. return false;
  144. if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE))
  145. return false;
  146. return true;
  147. }
  148. HANDLE
  149. create_event (const char *name, bool allow_all, bool initial_state, bool manual_reset)
  150. {
  151. if (allow_all)
  152. {
  153. struct security_attributes sa;
  154. if (!init_security_attributes_allow_all (&sa))
  155. return NULL;
  156. return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name);
  157. }
  158. else
  159. return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name);
  160. }
  161. void
  162. close_if_open (HANDLE h)
  163. {
  164. if (h != NULL)
  165. CloseHandle (h);
  166. }
  167. static bool
  168. match (const WIN32_FIND_DATA *find, const char *ext)
  169. {
  170. int i;
  171. if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  172. return false;
  173. if (!strlen (ext))
  174. return true;
  175. i = strlen (find->cFileName) - strlen (ext) - 1;
  176. if (i < 1)
  177. return false;
  178. return find->cFileName[i] == '.' && !_stricmp (find->cFileName + i + 1, ext);
  179. }
  180. /*
  181. * Modify the extension on a filename.
  182. */
  183. static bool
  184. modext (char *dest, int size, const char *src, const char *newext)
  185. {
  186. int i;
  187. if (size > 0 && (strlen (src) + 1) <= size)
  188. {
  189. strcpy (dest, src);
  190. dest [size - 1] = '\0';
  191. i = strlen (dest);
  192. while (--i >= 0)
  193. {
  194. if (dest[i] == '\\')
  195. break;
  196. if (dest[i] == '.')
  197. {
  198. dest[i] = '\0';
  199. break;
  200. }
  201. }
  202. if (strlen (dest) + strlen(newext) + 2 <= size)
  203. {
  204. strcat (dest, ".");
  205. strcat (dest, newext);
  206. return true;
  207. }
  208. dest [0] = '\0';
  209. }
  210. return false;
  211. }
  212. VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
  213. {
  214. char exe_path[MAX_PATH];
  215. char config_dir[MAX_PATH];
  216. char ext_string[16];
  217. char log_dir[MAX_PATH];
  218. char priority_string[64];
  219. char append_string[2];
  220. DWORD priority;
  221. bool append;
  222. ResetError ();
  223. if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
  224. {
  225. MSG (M_ERR, "ReportStatusToSCMgr #1 failed");
  226. goto finish;
  227. }
  228. /*
  229. * Create our exit event
  230. */
  231. exit_event = create_event (EXIT_EVENT_NAME, false, false, true);
  232. if (!exit_event)
  233. {
  234. MSG (M_ERR, "CreateEvent failed");
  235. goto finish;
  236. }
  237. /*
  238. * If exit event is already signaled, it means we were not
  239. * shut down properly.
  240. */
  241. if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT)
  242. {
  243. MSG (M_ERR, "Exit event is already signaled -- we were not shut down properly");
  244. goto finish;
  245. }
  246. if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
  247. {
  248. MSG (M_ERR, "ReportStatusToSCMgr #2 failed");
  249. goto finish;
  250. }
  251. /*
  252. * Read info from registry in key HKLM\SOFTWARE\OpenVPN
  253. */
  254. {
  255. HKEY openvpn_key;
  256. LONG status;
  257. DWORD len;
  258. DWORD type;
  259. static const char error_format_str[] =
  260. "Error querying registry key of type REG_SZ: HKLM\\" REG_KEY "\\%s";
  261. static const char error_format_dword[] =
  262. "Error querying registry key of type REG_DWORD: HKLM\\" REG_KEY "\\%s";
  263. status = RegOpenKeyEx(
  264. HKEY_LOCAL_MACHINE,
  265. REG_KEY,
  266. 0,
  267. KEY_READ,
  268. &openvpn_key);
  269. if (status != ERROR_SUCCESS)
  270. {
  271. SetLastError (status);
  272. MSG (M_SYSERR, "Registry key HKLM\\" REG_KEY " not found");
  273. goto finish;
  274. }
  275. /* get path to openvpn.exe */
  276. QUERY_REG_STRING ("exe_path", exe_path);
  277. /* get path to configuration directory */
  278. QUERY_REG_STRING ("config_dir", config_dir);
  279. /* get extension on configuration files */
  280. QUERY_REG_STRING ("config_ext", ext_string);
  281. /* get path to log directory */
  282. QUERY_REG_STRING ("log_dir", log_dir);
  283. /* get priority for spawned OpenVPN subprocesses */
  284. QUERY_REG_STRING ("priority", priority_string);
  285. /* should we truncate or append to logfile? */
  286. QUERY_REG_STRING ("log_append", append_string);
  287. RegCloseKey (openvpn_key);
  288. }
  289. /* set process priority */
  290. priority = NORMAL_PRIORITY_CLASS;
  291. if (!_stricmp (priority_string, "IDLE_PRIORITY_CLASS"))
  292. priority = IDLE_PRIORITY_CLASS;
  293. else if (!_stricmp (priority_string, "BELOW_NORMAL_PRIORITY_CLASS"))
  294. priority = BELOW_NORMAL_PRIORITY_CLASS;
  295. else if (!_stricmp (priority_string, "NORMAL_PRIORITY_CLASS"))
  296. priority = NORMAL_PRIORITY_CLASS;
  297. else if (!_stricmp (priority_string, "ABOVE_NORMAL_PRIORITY_CLASS"))
  298. priority = ABOVE_NORMAL_PRIORITY_CLASS;
  299. else if (!_stricmp (priority_string, "HIGH_PRIORITY_CLASS"))
  300. priority = HIGH_PRIORITY_CLASS;
  301. else
  302. {
  303. MSG (M_ERR, "Unknown priority name: %s", priority_string);
  304. goto finish;
  305. }
  306. /* set log file append/truncate flag */
  307. append = false;
  308. if (append_string[0] == '0')
  309. append = false;
  310. else if (append_string[0] == '1')
  311. append = true;
  312. else
  313. {
  314. MSG (M_ERR, "Log file append flag (given as '%s') must be '0' or '1'", append_string);
  315. goto finish;
  316. }
  317. /*
  318. * Instantiate an OpenVPN process for each configuration
  319. * file found.
  320. */
  321. {
  322. WIN32_FIND_DATA find_obj;
  323. HANDLE find_handle;
  324. BOOL more_files;
  325. char find_string[MAX_PATH];
  326. openvpn_snprintf (find_string, MAX_PATH, "%s\\*", config_dir);
  327. find_handle = FindFirstFile (find_string, &find_obj);
  328. if (find_handle == INVALID_HANDLE_VALUE)
  329. {
  330. MSG (M_ERR, "Cannot get configuration file list using: %s", find_string);
  331. goto finish;
  332. }
  333. /*
  334. * Loop over each config file
  335. */
  336. do {
  337. HANDLE log_handle = NULL;
  338. STARTUPINFO start_info;
  339. PROCESS_INFORMATION proc_info;
  340. struct security_attributes sa;
  341. char log_file[MAX_PATH];
  342. char log_path[MAX_PATH];
  343. char command_line[256];
  344. CLEAR (start_info);
  345. CLEAR (proc_info);
  346. CLEAR (sa);
  347. if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
  348. {
  349. MSG (M_ERR, "ReportStatusToSCMgr #3 failed");
  350. FindClose (find_handle);
  351. goto finish;
  352. }
  353. /* does file have the correct type and extension? */
  354. if (match (&find_obj, ext_string))
  355. {
  356. /* get log file pathname */
  357. if (!modext (log_file, sizeof (log_file), find_obj.cFileName, "log"))
  358. {
  359. MSG (M_ERR, "Cannot construct logfile name based on: %s", find_obj.cFileName);
  360. FindClose (find_handle);
  361. goto finish;
  362. }
  363. openvpn_snprintf (log_path, sizeof(log_path),
  364. "%s\\%s", log_dir, log_file);
  365. /* construct command line */
  366. openvpn_snprintf (command_line, sizeof(command_line), PACKAGE " --service %s 1 --config \"%s\"",
  367. EXIT_EVENT_NAME,
  368. find_obj.cFileName);
  369. /* Make security attributes struct for logfile handle so it can
  370. be inherited. */
  371. if (!init_security_attributes_allow_all (&sa))
  372. {
  373. MSG (M_SYSERR, "InitializeSecurityDescriptor start_" PACKAGE " failed");
  374. goto finish;
  375. }
  376. /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */
  377. log_handle = CreateFile (log_path,
  378. GENERIC_WRITE,
  379. FILE_SHARE_READ,
  380. &sa.sa,
  381. append ? OPEN_ALWAYS : CREATE_ALWAYS,
  382. FILE_ATTRIBUTE_NORMAL,
  383. NULL);
  384. if (log_handle == INVALID_HANDLE_VALUE)
  385. {
  386. MSG (M_SYSERR, "Cannot open logfile: %s", log_path);
  387. FindClose (find_handle);
  388. goto finish;
  389. }
  390. /* append to logfile? */
  391. if (append)
  392. {
  393. if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  394. {
  395. MSG (M_SYSERR, "Cannot seek to end of logfile: %s", log_path);
  396. FindClose (find_handle);
  397. goto finish;
  398. }
  399. }
  400. /* fill in STARTUPINFO struct */
  401. GetStartupInfo(&start_info);
  402. start_info.cb = sizeof(start_info);
  403. start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
  404. start_info.wShowWindow = SW_HIDE;
  405. start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  406. start_info.hStdOutput = start_info.hStdError = log_handle;
  407. /* create an OpenVPN process for one config file */
  408. if (!CreateProcess(exe_path,
  409. command_line,
  410. NULL,
  411. NULL,
  412. TRUE,
  413. priority | CREATE_NEW_CONSOLE,
  414. NULL,
  415. config_dir,
  416. &start_info,
  417. &proc_info))
  418. {
  419. MSG (M_SYSERR, "CreateProcess failed, exe='%s' cmdline='%s' dir='%s'",
  420. exe_path,
  421. command_line,
  422. config_dir);
  423. FindClose (find_handle);
  424. CloseHandle (log_handle);
  425. goto finish;
  426. }
  427. /* close unneeded handles */
  428. Sleep (1000); /* try to prevent race if we close logfile
  429. handle before child process DUPs it */
  430. if (!CloseHandle (proc_info.hProcess)
  431. || !CloseHandle (proc_info.hThread)
  432. || !CloseHandle (log_handle))
  433. {
  434. MSG (M_SYSERR, "CloseHandle failed");
  435. goto finish;
  436. }
  437. }
  438. /* more files to process? */
  439. more_files = FindNextFile (find_handle, &find_obj);
  440. } while (more_files);
  441. FindClose (find_handle);
  442. }
  443. /* we are now fully started */
  444. if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
  445. {
  446. MSG (M_ERR, "ReportStatusToSCMgr SERVICE_RUNNING failed");
  447. goto finish;
  448. }
  449. /* wait for our shutdown signal */
  450. if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0)
  451. {
  452. MSG (M_ERR, "wait for shutdown signal failed");
  453. }
  454. finish:
  455. ServiceStop ();
  456. if (exit_event)
  457. CloseHandle (exit_event);
  458. }
  459. VOID ServiceStop()
  460. {
  461. if (exit_event)
  462. SetEvent(exit_event);
  463. }