1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2016, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
9 * \brief Entry points for running/configuring Tor as Windows Service.
19 #ifdef HAVE_EVENT2_EVENT_H
20 #include <event2/event.h>
26 #define GENSRV_SERVICENAME "tor"
27 #define GENSRV_DISPLAYNAME "Tor Win32 Service"
28 #define GENSRV_DESCRIPTION \
29 "Provides an anonymous Internet communication system"
30 #define GENSRV_USERACCT "NT AUTHORITY\\LocalService"
32 // Cheating: using the pre-defined error codes, tricks Windows into displaying
33 // a semi-related human-readable error message if startup fails as
34 // opposed to simply scaring people with Error: 0xffffffff
35 #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
37 static SERVICE_STATUS service_status
;
38 static SERVICE_STATUS_HANDLE hStatus
;
40 /* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
41 * is a job for arguments, not globals. Alas, some of the functions that
42 * use them use them need to have fixed signatures, so they can be passed
43 * to the NT service functions. */
44 static char **backup_argv
;
45 static int backup_argc
;
47 static void nt_service_control(DWORD request
);
48 static void nt_service_body(int argc
, char **argv
);
49 static void nt_service_main(void);
50 static SC_HANDLE
nt_service_open_scm(void);
51 static SC_HANDLE
nt_service_open(SC_HANDLE hSCManager
);
52 static int nt_service_start(SC_HANDLE hService
);
53 static int nt_service_stop(SC_HANDLE hService
);
54 static int nt_service_install(int argc
, char **argv
);
55 static int nt_service_remove(void);
56 static int nt_service_cmd_start(void);
57 static int nt_service_cmd_stop(void);
59 /** Struct to hold dynamically loaded NT-service related function pointers.
65 /** Function pointers for Windows API functions related to service
66 * management. These are NULL, or they point to the . They're set by
67 * calling the LOAD macro below. */
69 BOOL (WINAPI
*ChangeServiceConfig2A_fn
)(
74 BOOL (WINAPI
*CloseServiceHandle_fn
)(
77 BOOL (WINAPI
*ControlService_fn
)(
80 LPSERVICE_STATUS lpServiceStatus
);
82 SC_HANDLE (WINAPI
*CreateServiceA_fn
)(
86 DWORD dwDesiredAccess
,
90 LPCSTR lpBinaryPathName
,
91 LPCSTR lpLoadOrderGroup
,
93 LPCSTR lpDependencies
,
94 LPCSTR lpServiceStartName
,
97 BOOL (WINAPI
*DeleteService_fn
)(
100 SC_HANDLE (WINAPI
*OpenSCManagerA_fn
)(
101 LPCSTR lpMachineName
,
102 LPCSTR lpDatabaseName
,
103 DWORD dwDesiredAccess
);
105 SC_HANDLE (WINAPI
*OpenServiceA_fn
)(
106 SC_HANDLE hSCManager
,
107 LPCSTR lpServiceName
,
108 DWORD dwDesiredAccess
);
110 BOOL (WINAPI
*QueryServiceStatus_fn
)(
112 LPSERVICE_STATUS lpServiceStatus
);
114 SERVICE_STATUS_HANDLE (WINAPI
*RegisterServiceCtrlHandlerA_fn
)(
115 LPCSTR lpServiceName
,
116 LPHANDLER_FUNCTION lpHandlerProc
);
118 BOOL (WINAPI
*SetServiceStatus_fn
)(SERVICE_STATUS_HANDLE
,
121 BOOL (WINAPI
*StartServiceCtrlDispatcherA_fn
)(
122 const SERVICE_TABLE_ENTRYA
* lpServiceTable
);
124 BOOL (WINAPI
*StartServiceA_fn
)(
126 DWORD dwNumServiceArgs
,
127 LPCSTR
* lpServiceArgVectors
);
129 BOOL (WINAPI
*LookupAccountNameA_fn
)(
131 LPCSTR lpAccountName
,
134 LPTSTR ReferencedDomainName
,
135 LPDWORD cchReferencedDomainName
,
136 PSID_NAME_USE peUse
);
139 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
140 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
143 /** Loads functions used by NT services. Returns on success, or prints a
144 * complaint to stdout and exits on error. */
146 nt_service_loadlibrary(void)
151 if (service_fns
.loaded
)
154 if (!(library
= load_windows_system_library(TEXT("advapi32.dll")))) {
155 log_err(LD_GENERAL
, "Couldn't open advapi32.dll. Are you trying to use "
156 "NT services on Windows 98? That doesn't work.");
160 /* Helper macro: try to load a function named <b>f</b> from "library" into
161 * service_functions.<b>f</b>_fn. On failure, log an error message, and goto
164 #define LOAD(f) STMT_BEGIN \
165 if (!(fn = GetProcAddress(library, #f))) { \
167 "Couldn't find %s in advapi32.dll! We probably got the " \
168 "name wrong.", #f); \
171 service_fns.f ## _fn = fn; \
175 LOAD(ChangeServiceConfig2A
);
176 LOAD(CloseServiceHandle
);
177 LOAD(ControlService
);
178 LOAD(CreateServiceA
);
180 LOAD(OpenSCManagerA
);
182 LOAD(QueryServiceStatus
);
183 LOAD(RegisterServiceCtrlHandlerA
);
184 LOAD(SetServiceStatus
);
185 LOAD(StartServiceCtrlDispatcherA
);
187 LOAD(LookupAccountNameA
);
189 service_fns
.loaded
= 1;
193 printf("Unable to load library support for NT services: exiting.\n");
197 /** If we're compiled to run as an NT service, and the service wants to
198 * shut down, then change our current status and return 1. Else
202 nt_service_is_stopping(void)
204 /* If we haven't loaded the function pointers, we can't possibly be an NT
205 * service trying to shut down. */
206 if (!service_fns
.loaded
)
209 if (service_status
.dwCurrentState
== SERVICE_STOP_PENDING
) {
210 service_status
.dwWin32ExitCode
= 0;
211 service_status
.dwCurrentState
= SERVICE_STOPPED
;
212 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
214 } else if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
220 /** Set the dwCurrentState field for our service to <b>state</b>. */
222 nt_service_set_state(DWORD state
)
224 service_status
.dwCurrentState
= state
;
227 /** Handles service control requests, such as stopping or starting the
230 nt_service_control(DWORD request
)
232 static struct timeval exit_now
;
234 exit_now
.tv_usec
= 0;
236 nt_service_loadlibrary();
239 case SERVICE_CONTROL_STOP
:
240 case SERVICE_CONTROL_SHUTDOWN
:
241 log_notice(LD_GENERAL
,
242 "Got stop/shutdown request; shutting down cleanly.");
243 service_status
.dwCurrentState
= SERVICE_STOP_PENDING
;
244 event_base_loopexit(tor_libevent_get_base(), &exit_now
);
247 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
250 /** Called when the service is started via the system's service control
251 * manager. This calls tor_init() and starts the main event loop. If
252 * tor_init() fails, the service will be stopped and exit code set to
253 * NT_SERVICE_ERROR_TORINIT_FAILED. */
255 nt_service_body(int argc
, char **argv
)
258 (void) argc
; /* unused */
259 (void) argv
; /* unused */
260 nt_service_loadlibrary();
261 service_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
262 service_status
.dwCurrentState
= SERVICE_START_PENDING
;
263 service_status
.dwControlsAccepted
=
264 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
265 service_status
.dwWin32ExitCode
= 0;
266 service_status
.dwServiceSpecificExitCode
= 0;
267 service_status
.dwCheckPoint
= 0;
268 service_status
.dwWaitHint
= 1000;
269 hStatus
= service_fns
.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME
,
270 (LPHANDLER_FUNCTION
) nt_service_control
);
273 /* Failed to register the service control handler function */
277 r
= tor_init(backup_argc
, backup_argv
);
279 /* Failed to start the Tor service */
280 r
= NT_SERVICE_ERROR_TORINIT_FAILED
;
281 service_status
.dwCurrentState
= SERVICE_STOPPED
;
282 service_status
.dwWin32ExitCode
= r
;
283 service_status
.dwServiceSpecificExitCode
= r
;
284 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
288 /* Set the service's status to SERVICE_RUNNING and start the main
290 service_status
.dwCurrentState
= SERVICE_RUNNING
;
291 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
296 /** Main service entry point. Starts the service control dispatcher and waits
297 * until the service status is set to SERVICE_STOPPED. */
299 nt_service_main(void)
301 SERVICE_TABLE_ENTRYA table
[2];
304 nt_service_loadlibrary();
305 table
[0].lpServiceName
= (char*)GENSRV_SERVICENAME
;
306 table
[0].lpServiceProc
= (LPSERVICE_MAIN_FUNCTIONA
)nt_service_body
;
307 table
[1].lpServiceName
= NULL
;
308 table
[1].lpServiceProc
= NULL
;
310 if (!service_fns
.StartServiceCtrlDispatcherA_fn(table
)) {
311 result
= GetLastError();
312 errmsg
= format_win32_error(result
);
313 printf("Service error %d : %s\n", (int) result
, errmsg
);
315 if (result
== ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
) {
316 if (tor_init(backup_argc
, backup_argv
) < 0)
318 switch (get_options()->command
) {
322 case CMD_LIST_FINGERPRINT
:
323 case CMD_HASH_PASSWORD
:
324 case CMD_VERIFY_CONFIG
:
325 case CMD_DUMP_CONFIG
:
327 log_err(LD_CONFIG
, "Unsupported command (--list-fingerprint, "
328 "--hash-password, --keygen, --dump-config, or --verify-config) "
331 case CMD_RUN_UNITTESTS
:
333 log_err(LD_CONFIG
, "Illegal command number %d: internal error.",
334 get_options()->command
);
341 /** Return a handle to the service control manager on success, or NULL on
344 nt_service_open_scm(void)
346 SC_HANDLE hSCManager
;
349 nt_service_loadlibrary();
350 if ((hSCManager
= service_fns
.OpenSCManagerA_fn(
351 NULL
, NULL
, SC_MANAGER_CREATE_SERVICE
)) == NULL
) {
352 errmsg
= format_win32_error(GetLastError());
353 printf("OpenSCManager() failed : %s\n", errmsg
);
359 /** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
362 nt_service_open(SC_HANDLE hSCManager
)
366 nt_service_loadlibrary();
367 if ((hService
= service_fns
.OpenServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
368 SERVICE_ALL_ACCESS
)) == NULL
) {
369 errmsg
= format_win32_error(GetLastError());
370 printf("OpenService() failed : %s\n", errmsg
);
376 /** Start the Tor service. Return 0 if the service is started or was
377 * previously running. Return -1 on error. */
379 nt_service_start(SC_HANDLE hService
)
383 nt_service_loadlibrary();
385 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
386 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
387 printf("Service is already running\n");
391 if (service_fns
.StartServiceA_fn(hService
, 0, NULL
)) {
392 /* Loop until the service has finished attempting to start */
393 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
394 (service_status
.dwCurrentState
== SERVICE_START_PENDING
)) {
398 /* Check if it started successfully or not */
399 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
400 printf("Service started successfully\n");
403 errmsg
= format_win32_error(service_status
.dwWin32ExitCode
);
404 printf("Service failed to start : %s\n", errmsg
);
408 errmsg
= format_win32_error(GetLastError());
409 printf("StartService() failed : %s\n", errmsg
);
415 /** Stop the Tor service. Return 0 if the service is stopped or was not
416 * previously running. Return -1 on error. */
418 nt_service_stop(SC_HANDLE hService
)
420 /** Wait at most 10 seconds for the service to stop. */
421 #define MAX_SERVICE_WAIT_TIME 10
424 nt_service_loadlibrary();
426 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
427 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
428 printf("Service is already stopped\n");
432 if (service_fns
.ControlService_fn(hService
, SERVICE_CONTROL_STOP
,
435 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
436 (service_status
.dwCurrentState
!= SERVICE_STOPPED
) &&
437 (wait_time
< MAX_SERVICE_WAIT_TIME
)) {
441 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
442 printf("Service stopped successfully\n");
444 } else if (wait_time
== MAX_SERVICE_WAIT_TIME
) {
445 printf("Service did not stop within %d seconds.\n", wait_time
);
447 errmsg
= format_win32_error(GetLastError());
448 printf("QueryServiceStatus() failed : %s\n",errmsg
);
452 errmsg
= format_win32_error(GetLastError());
453 printf("ControlService() failed : %s\n", errmsg
);
459 /** Build a formatted command line used for the NT service. Return a
460 * pointer to the formatted string on success, or NULL on failure. Set
461 * *<b>using_default_torrc</b> to true if we're going to use the default
462 * location to torrc, or 1 if an option was specified on the command line.
465 nt_service_command_line(int *using_default_torrc
)
467 TCHAR tor_exe
[MAX_PATH
+1];
468 char tor_exe_ascii
[MAX_PATH
*2+1];
469 char *command
=NULL
, *options
=NULL
;
472 *using_default_torrc
= 1;
474 /* Get the location of tor.exe */
475 if (0 == GetModuleFileName(NULL
, tor_exe
, MAX_PATH
))
478 /* Get the service arguments */
479 sl
= smartlist_new();
480 for (i
= 1; i
< backup_argc
; ++i
) {
481 if (!strcmp(backup_argv
[i
], "--options") ||
482 !strcmp(backup_argv
[i
], "-options")) {
483 while (++i
< backup_argc
) {
484 if (!strcmp(backup_argv
[i
], "-f"))
485 *using_default_torrc
= 0;
486 smartlist_add(sl
, backup_argv
[i
]);
490 if (smartlist_len(sl
))
491 options
= smartlist_join_strings(sl
,"\" \"",0,NULL
);
495 wcstombs(tor_exe_ascii
, tor_exe
, sizeof(tor_exe_ascii
));
496 tor_exe_ascii
[sizeof(tor_exe_ascii
)-1] = '\0';
498 strlcpy(tor_exe_ascii
, tor_exe
, sizeof(tor_exe_ascii
));
501 /* Allocate a string for the NT service command line and */
502 /* Format the service command */
504 tor_asprintf(&command
, "\"%s\" --nt-service \"%s\"",
505 tor_exe_ascii
, options
);
506 } else { /* ! options */
507 tor_asprintf(&command
, "\"%s\" --nt-service", tor_exe_ascii
);
514 /** Creates a Tor NT service, set to start on boot. The service will be
515 * started if installation succeeds. Returns 0 on success, or -1 on
518 nt_service_install(int argc
, char **argv
)
520 /* Notes about developing NT services:
522 * 1. Don't count on your CWD. If an absolute path is not given, the
523 * fopen() function goes wrong.
524 * 2. The parameters given to the nt_service_body() function differ
525 * from those given to main() function.
528 SC_HANDLE hSCManager
= NULL
;
529 SC_HANDLE hService
= NULL
;
530 SERVICE_DESCRIPTIONA sdBuff
;
533 const char *user_acct
= NULL
;
534 const char *password
= "";
536 OSVERSIONINFOEX info
;
538 DWORD sidLen
= 0, domainLen
= 0;
539 int is_win2k_or_worse
= 0;
540 int using_default_torrc
= 0;
542 nt_service_loadlibrary();
544 /* Open the service control manager so we can create a new service */
545 if ((hSCManager
= nt_service_open_scm()) == NULL
)
547 /* Build the command line used for the service */
548 if ((command
= nt_service_command_line(&using_default_torrc
)) == NULL
) {
549 printf("Unable to build service command line.\n");
550 service_fns
.CloseServiceHandle_fn(hSCManager
);
554 for (i
=1; i
< argc
; ++i
) {
555 if (!strcmp(argv
[i
], "--user") && i
+1<argc
) {
556 user_acct
= argv
[i
+1];
559 if (!strcmp(argv
[i
], "--password") && i
+1<argc
) {
560 password
= argv
[i
+1];
565 /* Compute our version and see whether we're running win2k or earlier. */
566 memset(&info
, 0, sizeof(info
));
567 info
.dwOSVersionInfoSize
= sizeof(info
);
568 if (! GetVersionEx((LPOSVERSIONINFO
)&info
)) {
569 printf("Call to GetVersionEx failed.\n");
570 is_win2k_or_worse
= 1;
572 if (info
.dwMajorVersion
< 5 ||
573 (info
.dwMajorVersion
== 5 && info
.dwMinorVersion
== 0))
574 is_win2k_or_worse
= 1;
578 if (is_win2k_or_worse
) {
579 /* On Win2k, there is no LocalService account, so we actually need to
580 * fall back on NULL (the system account). */
581 printf("Running on Win2K or earlier, so the LocalService account "
582 "doesn't exist. Falling back to SYSTEM account.\n");
584 /* Genericity is apparently _so_ last year in Redmond, where some
585 * accounts are accounts that you can look up, and some accounts
586 * are magic and undetectable via the security subsystem. See
587 * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
589 printf("Running on a Post-Win2K OS, so we'll assume that the "
590 "LocalService account exists.\n");
591 user_acct
= GENSRV_USERACCT
;
593 } else if (0 && service_fns
.LookupAccountNameA_fn(NULL
, // On this system
595 NULL
, &sidLen
, // Don't care about the SID
596 NULL
, &domainLen
, // Don't care about the domain
598 /* XXXX For some reason, the above test segfaults. Fix that. */
599 printf("User \"%s\" doesn't seem to exist.\n", user_acct
);
602 printf("Will try to install service as user \"%s\".\n", user_acct
);
604 /* XXXX This warning could be better about explaining how to resolve the
606 if (using_default_torrc
)
607 printf("IMPORTANT NOTE:\n"
608 " The Tor service will run under the account \"%s\". This means\n"
609 " that Tor will look for its configuration file under that\n"
610 " account's Application Data directory, which is probably not\n"
611 " the same as yours.\n", user_acct
?user_acct
:"<local system>");
613 /* Create the Tor service, set to auto-start on boot */
614 if ((hService
= service_fns
.CreateServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
616 SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
,
617 SERVICE_AUTO_START
, SERVICE_ERROR_IGNORE
,
618 command
, NULL
, NULL
, NULL
,
619 user_acct
, password
)) == NULL
) {
620 errmsg
= format_win32_error(GetLastError());
621 printf("CreateService() failed : %s\n", errmsg
);
622 service_fns
.CloseServiceHandle_fn(hSCManager
);
627 printf("Done with CreateService.\n");
629 /* Set the service's description */
630 sdBuff
.lpDescription
= (char*)GENSRV_DESCRIPTION
;
631 service_fns
.ChangeServiceConfig2A_fn(hService
, SERVICE_CONFIG_DESCRIPTION
,
633 printf("Service installed successfully\n");
635 /* Start the service initially */
636 nt_service_start(hService
);
638 service_fns
.CloseServiceHandle_fn(hService
);
639 service_fns
.CloseServiceHandle_fn(hSCManager
);
645 /** Removes the Tor NT service. Returns 0 if the service was successfully
646 * removed, or -1 on error. */
648 nt_service_remove(void)
650 SC_HANDLE hSCManager
= NULL
;
651 SC_HANDLE hService
= NULL
;
654 nt_service_loadlibrary();
655 if ((hSCManager
= nt_service_open_scm()) == NULL
)
657 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
658 service_fns
.CloseServiceHandle_fn(hSCManager
);
662 nt_service_stop(hService
);
663 if (service_fns
.DeleteService_fn(hService
) == FALSE
) {
664 errmsg
= format_win32_error(GetLastError());
665 printf("DeleteService() failed : %s\n", errmsg
);
667 service_fns
.CloseServiceHandle_fn(hService
);
668 service_fns
.CloseServiceHandle_fn(hSCManager
);
672 service_fns
.CloseServiceHandle_fn(hService
);
673 service_fns
.CloseServiceHandle_fn(hSCManager
);
674 printf("Service removed successfully\n");
679 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
681 nt_service_cmd_start(void)
683 SC_HANDLE hSCManager
;
687 if ((hSCManager
= nt_service_open_scm()) == NULL
)
689 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
690 service_fns
.CloseServiceHandle_fn(hSCManager
);
694 start
= nt_service_start(hService
);
695 service_fns
.CloseServiceHandle_fn(hService
);
696 service_fns
.CloseServiceHandle_fn(hSCManager
);
701 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
703 nt_service_cmd_stop(void)
705 SC_HANDLE hSCManager
;
709 if ((hSCManager
= nt_service_open_scm()) == NULL
)
711 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
712 service_fns
.CloseServiceHandle_fn(hSCManager
);
716 stop
= nt_service_stop(hService
);
717 service_fns
.CloseServiceHandle_fn(hService
);
718 service_fns
.CloseServiceHandle_fn(hSCManager
);
724 nt_service_parse_options(int argc
, char **argv
, int *should_exit
)
731 (!strcmp(argv
[1], "-service") || !strcmp(argv
[1], "--service"))) {
732 nt_service_loadlibrary();
734 if (!strcmp(argv
[2], "install"))
735 return nt_service_install(argc
, argv
);
736 if (!strcmp(argv
[2], "remove"))
737 return nt_service_remove();
738 if (!strcmp(argv
[2], "start"))
739 return nt_service_cmd_start();
740 if (!strcmp(argv
[2], "stop"))
741 return nt_service_cmd_stop();
742 printf("Unrecognized service command '%s'\n", argv
[2]);
746 if (!strcmp(argv
[1], "-nt-service") || !strcmp(argv
[1], "--nt-service")) {
747 nt_service_loadlibrary();
752 // These values have been deprecated since 0.1.1.2-alpha; we've warned
753 // about them since 0.1.2.7-alpha.
754 if (!strcmp(argv
[1], "-install") || !strcmp(argv
[1], "--install")) {
755 nt_service_loadlibrary();
757 "The %s option is deprecated; use \"--service install\" instead.",
760 return nt_service_install(argc
, argv
);
762 if (!strcmp(argv
[1], "-remove") || !strcmp(argv
[1], "--remove")) {
763 nt_service_loadlibrary();
765 "The %s option is deprecated; use \"--service remove\" instead.",
768 return nt_service_remove();