suricata
win32-service.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2010 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Ondrej Slanina <oslanina@kerio.com>
22  *
23  * Windows service functions
24  */
25 
26 #ifdef OS_WIN32
27 
28 #include "suricata-common.h"
29 #include "suricata.h"
30 #include "win32-service.h"
31 #include "util-debug.h"
32 
33 #include <sddl.h>
34 
35 /* The default service DACL, but without SERVICE_USER_DEFINED_CONTROL (CR) for
36  * interactive (IU) and service (SU) users, so only SYSTEM and Administrators
37  * can send user-defined control codes. */
38 #define SERVICE_SDDL \
39  "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)" \
40  "(A;;CCLCSWLORC;;;IU)(A;;CCLCSWLORC;;;SU)"
41 
42 /** Append a character, leaving room for the terminating NUL. */
43 static bool SCServiceAppendChar(char *path, size_t size, size_t *len, char c)
44 {
45  if (size == 0 || *len >= size - 1) {
46  return false;
47  }
48  path[(*len)++] = c;
49  path[*len] = '\0';
50  return true;
51 }
52 
53 /**
54  * Quote an argument for the Windows C runtime. Backslashes are literal except
55  * before a double quote: emit 2n+1 backslashes before an embedded quote, and
56  * 2n before the closing quote. Always quote, including empty arguments.
57  */
58 static bool SCServiceAppendArgument(char *path, size_t size, size_t *len, const char *arg)
59 {
60  if (!SCServiceAppendChar(path, size, len, '"')) {
61  return false;
62  }
63  while (*arg != '\0') {
64  size_t backslashes = 0;
65  while (*arg == '\\') {
66  backslashes++;
67  arg++;
68  }
69  for (size_t i = 0; i < backslashes; i++) {
70  if (!SCServiceAppendChar(path, size, len, '\\')) {
71  return false;
72  }
73  if ((*arg == '"' || *arg == '\0') && !SCServiceAppendChar(path, size, len, '\\')) {
74  return false;
75  }
76  }
77  if (*arg == '\0') {
78  break;
79  }
80  if (*arg == '"' && !SCServiceAppendChar(path, size, len, '\\')) {
81  return false;
82  }
83  if (!SCServiceAppendChar(path, size, len, *arg++)) {
84  return false;
85  }
86  }
87  return SCServiceAppendChar(path, size, len, '"');
88 }
89 
90 /** Build the service command line without accessing SCM. */
91 static bool SCServiceBuildCommandLine(char *path, size_t size, const char *module, int argc,
92  const char *const *argv, const char *service_option)
93 {
94  if (size == 0) {
95  return false;
96  }
97  path[0] = '\0';
98  size_t len = 0;
99  if (!SCServiceAppendArgument(path, size, &len, module)) {
100  return false;
101  }
102  for (int i = 1; i < argc; i++) {
103  /* Preserve the existing service-option prefix matching. */
104  if (strlen(argv[i]) <= strlen(service_option) &&
105  strncmp(service_option, argv[i], strlen(argv[i])) == 0) {
106  continue;
107  }
108  if (!SCServiceAppendChar(path, size, &len, ' ') ||
109  !SCServiceAppendArgument(path, size, &len, argv[i])) {
110  return false;
111  }
112  }
113  return true;
114 }
115 
116 /** Resolve the executable and reject truncation before making any SCM calls. */
117 static bool SCServiceGetCommandLine(
118  char *path, size_t size, int argc, char **argv, const char *service_option)
119 {
120  char module[2048];
121  DWORD len = GetModuleFileName(NULL, module, sizeof(module));
122  if (len == 0) {
123  SCLogError("Can't get path to service binary: %d", (int)GetLastError());
124  return false;
125  }
126  if (len >= sizeof(module)) {
127  SCLogError("failed to construct service path string: binary path truncated");
128  return false;
129  }
130  if (!SCServiceBuildCommandLine(
131  path, size, module, argc, (const char *const *)argv, service_option)) {
132  SCLogError("failed to construct service path string: invalid or truncated command line");
133  return false;
134  }
135  return true;
136 }
137 
138 static SERVICE_STATUS_HANDLE service_status_handle = 0;
139 
140 /* Pending SC_SERVICE_REQUEST_* flags, handled by the main loop. */
141 static SC_ATOMIC_DECLARE(uint32_t, service_requests);
142 
143 static int service_argc = 0;
144 
145 static char **service_argv = NULL;
146 
147 static int service_initialized = 0;
148 
149 int main(int argc, char **argv);
150 
151 /**
152  * \brief Detect if running as service or console app
153  */
154 int SCRunningAsService(void)
155 {
156  HANDLE h = INVALID_HANDLE_VALUE;
157  if ((h = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
158  SCLogInfo("Running as service: yes");
159  return 1;
160  }
161  CloseHandle(h);
162  SCLogInfo("Running as service: no");
163  return 0;
164 }
165 
166 /**
167  * \brief Detect if running as service or console app
168  */
169 static void SCAtExitHandler(void)
170 {
171  SERVICE_STATUS status = {
172  SERVICE_WIN32,
173  SERVICE_STOPPED,
174  SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
175  NO_ERROR,
176  NO_ERROR,
177  0,
178  0
179  };
180 
181  SCLogInfo("Exit handler called.");
182 
183  /* mark service as stopped */
184  if (!SetServiceStatus(service_status_handle, &status)) {
185  SCLogWarning("Can't set service status: %d", (int)GetLastError());
186  } else {
187  SCLogInfo("Service status set to: SERVICE_STOPPED");
188  }
189 }
190 
191 /**
192  * \brief Get and clear the pending service control requests.
193  *
194  * \retval SC_SERVICE_REQUEST_* flags
195  */
196 uint32_t SCServiceTakeRequests(void)
197 {
198  return SC_ATOMIC_AND(service_requests, 0);
199 }
200 
201 /**
202  * \brief Service handler
203  */
204 static DWORD WINAPI SCServiceCtrlHandlerEx(DWORD code, DWORD etype, LPVOID edata, LPVOID context)
205 {
206  if (code == SC_SERVICE_CONTROL_RELOAD_RULES) {
207  /* Defer logging to the main loop, after logging initialization. */
208  SC_ATOMIC_OR(service_requests, SC_SERVICE_REQUEST_RELOAD_RULES);
209  return NO_ERROR;
210  }
211 
212  if (code == SC_SERVICE_CONTROL_REOPEN_LOG_FILES) {
213  /* Defer logging to the main loop, after logging initialization. */
214  SC_ATOMIC_OR(service_requests, SC_SERVICE_REQUEST_REOPEN_LOG_FILES);
215  return NO_ERROR;
216  }
217 
218  if (code == SERVICE_CONTROL_SHUTDOWN || code == SERVICE_CONTROL_STOP) {
219  SERVICE_STATUS status = {
220  SERVICE_WIN32,
221  SERVICE_STOP_PENDING,
222  SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
223  NO_ERROR,
224  NO_ERROR,
225  0,
226  0
227  };
228 
229  SCLogInfo("Service control handler called with %s control code.",
230  ((code == SERVICE_CONTROL_SHUTDOWN) ? ("SERVICE_CONTROL_SHUTDOWN") : ("SERVICE_CONTROL_STOP")));
231 
232  /* mark service as stop pending */
233  if (!SetServiceStatus(service_status_handle, &status)) {
234  SCLogWarning("Can't set service status: %d", (int)GetLastError());
235  } else {
236  SCLogInfo("Service status set to: SERVICE_STOP_PENDING");
237  }
238 
239  /* mark engine as stopping */
240  EngineStop();
241 
242  return NO_ERROR;
243  }
244 
245  return ERROR_CALL_NOT_IMPLEMENTED;
246 }
247 
248 /**
249  * \brief Service main function
250  */
251 static void WINAPI SCServiceMain(uint32_t argc, char** argv)
252 {
253  SERVICE_STATUS status = {
254  SERVICE_WIN32,
255  SERVICE_RUNNING,
256  SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
257  NO_ERROR,
258  NO_ERROR,
259  0,
260  0
261  };
262 
263  if ((service_status_handle = RegisterServiceCtrlHandlerEx((char *)PROG_NAME, SCServiceCtrlHandlerEx, NULL)) == (SERVICE_STATUS_HANDLE)0) {
264  SCLogError("Can't register service control handler: %d", (int)GetLastError());
265  return;
266  }
267 
268  /* register exit handler */
269  if (atexit(SCAtExitHandler)) {
270  SCLogWarning("Can't register exit handler: %d", (int)GetLastError());
271  }
272 
273  /* mark service as running immediately */
274  if (!SetServiceStatus(service_status_handle, &status)) {
275  SCLogWarning("Can't set service status: %d", (int)GetLastError());
276  } else {
277  SCLogInfo("Service status set to: SERVICE_RUNNING");
278  }
279 
280  SCLogInfo("Entering main function...");
281 
282  /* suricata initialization -> main loop -> uninitialization */
283  main(service_argc, service_argv);
284 
285  SCLogInfo("Leaving main function.");
286 
287  /* mark service as stopped */
288  status.dwCurrentState = SERVICE_STOPPED;
289 
290  if (!SetServiceStatus(service_status_handle, &status)) {
291  SCLogWarning("Can't set service status: %d", (int)GetLastError());
292  } else {
293  SCLogInfo("Service status set to: SERVICE_STOPPED");
294  }
295 }
296 
297 /**
298  * \brief Init suricata service
299  *
300  * \param argc num of arguments
301  * \param argv passed arguments
302  */
303 int SCServiceInit(int argc, char **argv)
304 {
305  SERVICE_TABLE_ENTRY DispatchTable[] = {
306  {(char *)PROG_NAME, (LPSERVICE_MAIN_FUNCTION) SCServiceMain},
307  {NULL, NULL}
308  };
309 
310  /* continue with suricata initialization */
311  if (service_initialized) {
312  SCLogWarning("Service is already initialized.");
313  return 0;
314  }
315 
316  /* save args */
317  service_argc = argc;
318  service_argv = argv;
319 
320  service_initialized = 1;
321 
322  SCLogInfo("Entering service control dispatcher...");
323 
324  if (!StartServiceCtrlDispatcher(DispatchTable)) {
325  /* exit with failure */
326  exit(EXIT_FAILURE);
327  }
328 
329  SCLogInfo("Leaving service control dispatcher.");
330 
331  /* exit with success */
332  exit(EXIT_SUCCESS);
333 }
334 
335 /**
336  * \brief Restrict who can send user-defined control codes to the service.
337  *
338  * By default, interactive and service users can send user-defined control
339  * codes to a service. Restrict this to SYSTEM and Administrators as these
340  * codes will be used for runtime control of Suricata, such as rule reloads.
341  */
342 static int SCServiceSetSecurity(SC_HANDLE service)
343 {
344  PSECURITY_DESCRIPTOR sd = NULL;
345  int ret = 0;
346 
347  if (!ConvertStringSecurityDescriptorToSecurityDescriptorA(
348  SERVICE_SDDL, SDDL_REVISION_1, &sd, NULL)) {
349  SCLogError("Can't create service security descriptor: %d", (int)GetLastError());
350  return -1;
351  }
352 
353  if (!SetServiceObjectSecurity(service, DACL_SECURITY_INFORMATION, sd)) {
354  SCLogError("Can't set service security descriptor: %d", (int)GetLastError());
355  ret = -1;
356  }
357 
358  LocalFree(sd);
359  return ret;
360 }
361 
362 /**
363  * \brief Install suricata as service
364  *
365  * \param argc num of arguments
366  * \param argv passed arguments
367  */
368 int SCServiceInstall(int argc, char **argv)
369 {
370  char path[2048];
371  SC_HANDLE service = NULL;
372  SC_HANDLE scm = NULL;
373  int ret = -1;
374 
375  do {
376  if (!SCServiceGetCommandLine(path, sizeof(path), argc, argv, "--service-install")) {
377  break;
378  }
379 
380  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
381  SCLogError("Can't open SCM: %d", (int)GetLastError());
382  break;
383  }
384 
385  service = CreateService(
386  scm,
387  PROG_NAME,
388  PROG_NAME,
389  SERVICE_ALL_ACCESS,
390  SERVICE_WIN32_OWN_PROCESS,
391  SERVICE_DEMAND_START,
392  SERVICE_ERROR_NORMAL,
393  path,
394  NULL,
395  NULL,
396  NULL,
397  NULL,
398  NULL);
399 
400  if (service == NULL) {
401  SCLogError("Can't create service: %d", (int)GetLastError());
402  break;
403  }
404 
405  if (SCServiceSetSecurity(service) != 0) {
406  DeleteService(service);
407  break;
408  }
409 
410  ret = 0;
411 
412  } while(0);
413 
414  if (service) {
415  CloseServiceHandle(service);
416  }
417 
418  if (scm) {
419  CloseServiceHandle(scm);
420  }
421 
422  return ret;
423 }
424 
425 /**
426  * \brief Remove suricata service
427  *
428  * \param argc num of arguments
429  * \param argv passed arguments
430  */
431 int SCServiceRemove(void)
432 {
433  SERVICE_STATUS status;
434  SC_HANDLE service = NULL;
435  SC_HANDLE scm = NULL;
436  int ret = -1;
437 
438  do {
439  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
440  SCLogError("Can't open SCM: %d", (int)GetLastError());
441  break;
442  }
443 
444  if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) {
445  SCLogError("Can't open service: %d", (int)GetLastError());
446  break;
447  }
448 
449  if (!QueryServiceStatus(service, &status)) {
450  SCLogError("Can't query service status: %d", (int)GetLastError());
451  break;
452  }
453 
454  if (status.dwCurrentState != SERVICE_STOPPED) {
455  SCLogError("Service isn't in stopped state: %d", (int)GetLastError());
456  break;
457  }
458 
459  if (!DeleteService(service)) {
460  SCLogError("Can't delete service: %d", (int)GetLastError());
461  break;
462  }
463 
464  ret = 0;
465 
466  } while(0);
467 
468  if (service) {
469  CloseServiceHandle(service);
470  }
471 
472  if (scm) {
473  CloseServiceHandle(scm);
474  }
475 
476  return ret;
477 }
478 
479 /**
480  * \brief Change suricata service startup parameters
481  *
482  * \param argc num of arguments
483  * \param argv passed arguments
484  */
485 int SCServiceChangeParams(int argc, char **argv)
486 {
487  char path[2048];
488  SC_HANDLE service = NULL;
489  SC_HANDLE scm = NULL;
490  int ret = -1;
491 
492  do {
493  if (!SCServiceGetCommandLine(path, sizeof(path), argc, argv, "--service-change-params")) {
494  break;
495  }
496 
497  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
498  SCLogError("Can't open SCM: %d", (int)GetLastError());
499  break;
500  }
501 
502  if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) {
503  SCLogError("Can't open service: %d", (int)GetLastError());
504  break;
505  }
506 
507  if (!ChangeServiceConfig(
508  service,
509  SERVICE_WIN32_OWN_PROCESS,
510  SERVICE_DEMAND_START,
511  SERVICE_ERROR_NORMAL,
512  path,
513  NULL,
514  NULL,
515  NULL,
516  NULL,
517  NULL,
518  PROG_NAME))
519  {
520  SCLogError("Can't change service configuration: %d", (int)GetLastError());
521  break;
522  }
523 
524  ret = 0;
525 
526  } while(0);
527 
528  return ret;
529 }
530 
531 #endif /* OS_WIN32 */
len
uint8_t len
Definition: app-layer-dnp3.h:2
util-debug.h
main
int main(int argc, char **argv)
Definition: main.c:20
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:262
SC_ATOMIC_DECLARE
#define SC_ATOMIC_DECLARE(type, name)
wrapper for declaring atomic variables.
Definition: util-atomic.h:280
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:232
PROG_NAME
#define PROG_NAME
Definition: suricata.h:75
suricata-common.h
EngineStop
void EngineStop(void)
make sure threads can stop the engine by calling this function. Purpose: pcap file mode needs to be a...
Definition: suricata.c:502
win32-service.h
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
suricata.h
SC_ATOMIC_AND
#define SC_ATOMIC_AND(name, val)
Bitwise AND a value to our atomic variable.
Definition: util-atomic.h:359
SC_ATOMIC_OR
#define SC_ATOMIC_OR(name, val)
Bitwise OR a value to our atomic variable.
Definition: util-atomic.h:350