Electroneum
windows_service.cpp
Go to the documentation of this file.
1 // Copyrights(c) 2017-2021, The Electroneum Project
2 // Copyrights(c) 2014-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include <boost/chrono/chrono.hpp>
31 #include <boost/thread/thread.hpp>
32 
33 #undef UNICODE
34 #undef _UNICODE
35 
38 #include "string_tools.h"
39 #include <chrono>
40 #include <iostream>
41 #include <utility>
42 #include <memory>
43 #include <shellapi.h>
44 #include <thread>
45 #include <windows.h>
46 
47 namespace windows {
48 
49 namespace {
50  typedef std::unique_ptr<std::remove_pointer<SC_HANDLE>::type, decltype(&::CloseServiceHandle)> service_handle;
51 
52  std::string get_last_error()
53  {
54  LPSTR p_error_text = nullptr;
55 
56  FormatMessage(
57  FORMAT_MESSAGE_FROM_SYSTEM
58  | FORMAT_MESSAGE_ALLOCATE_BUFFER
59  | FORMAT_MESSAGE_IGNORE_INSERTS
60  , nullptr
61  , GetLastError()
62  , MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
63  , reinterpret_cast<LPSTR>(&p_error_text)
64  , 0
65  , nullptr
66  );
67 
68  if (nullptr == p_error_text)
69  {
70  return "";
71  }
72  else
73  {
74  std::string ret{p_error_text};
75  LocalFree(p_error_text);
76  return ret;
77  }
78  }
79 
80  bool relaunch_as_admin(
81  std::string const & command
82  , std::string const & arguments
83  )
84  {
85  SHELLEXECUTEINFO info{};
86  info.cbSize = sizeof(info);
87  info.lpVerb = "runas";
88  info.lpFile = command.c_str();
89  info.lpParameters = arguments.c_str();
90  info.hwnd = nullptr;
91  info.nShow = SW_SHOWNORMAL;
92  if (!ShellExecuteEx(&info))
93  {
94  tools::fail_msg_writer() << "Admin relaunch failed: " << get_last_error();
95  return false;
96  }
97  else
98  {
99  return true;
100  }
101  }
102 
103  // When we relaunch as admin, Windows opens a new window. This just pauses
104  // to allow the user to read any output.
105  void pause_to_display_admin_window_messages()
106  {
107  boost::chrono::milliseconds how_long{1500};
108  boost::this_thread::sleep_for(how_long);
109  }
110 }
111 
112 bool check_admin(bool & result)
113 {
114  BOOL is_admin = FALSE;
115  PSID p_administrators_group = nullptr;
116 
117  SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
118 
119  if (!AllocateAndInitializeSid(
120  &nt_authority
121  , 2
122  , SECURITY_BUILTIN_DOMAIN_RID
123  , DOMAIN_ALIAS_RID_ADMINS
124  , 0, 0, 0, 0, 0, 0
125  , &p_administrators_group
126  ))
127  {
128  tools::fail_msg_writer() << "Security Identifier creation failed: " << get_last_error();
129  return false;
130  }
131 
132  if (!CheckTokenMembership(
133  nullptr
134  , p_administrators_group
135  , &is_admin
136  ))
137  {
138  tools::fail_msg_writer() << "Permissions check failed: " << get_last_error();
139  return false;
140  }
141 
142  result = is_admin ? true : false;
143 
144  return true;
145 }
146 
148  std::string const & arguments
149  )
150 {
151  bool admin;
152 
153  if (!check_admin(admin))
154  {
155  return false;
156  }
157 
158  if (admin)
159  {
160  return true;
161  }
162  else
163  {
164  std::string command = epee::string_tools::get_current_module_path();
165  relaunch_as_admin(command, arguments);
166  return false;
167  }
168 }
169 
171  std::string const & service_name
172  , std::string const & arguments
173  )
174 {
175  std::string command = epee::string_tools::get_current_module_path();
176  std::string full_command = command + arguments;
177 
178  service_handle p_manager{
179  OpenSCManager(
180  nullptr
181  , nullptr
182  , SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE
183  )
184  , &::CloseServiceHandle
185  };
186  if (p_manager == nullptr)
187  {
188  tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
189  return false;
190  }
191 
192  service_handle p_service{
193  CreateService(
194  p_manager.get()
195  , service_name.c_str()
196  , service_name.c_str()
197  , 0
198  //, GENERIC_EXECUTE | GENERIC_READ
199  , SERVICE_WIN32_OWN_PROCESS
200  , SERVICE_DEMAND_START
201  , SERVICE_ERROR_NORMAL
202  , full_command.c_str()
203  , nullptr
204  , nullptr
205  , ""
206  //, "NT AUTHORITY\\LocalService"
207  , nullptr // Implies LocalSystem account
208  , nullptr
209  )
210  , &::CloseServiceHandle
211  };
212  if (p_service == nullptr)
213  {
214  tools::fail_msg_writer() << "Couldn't create service: " << get_last_error();
215  return false;
216  }
217 
218  tools::success_msg_writer() << "Service installed";
219 
220  pause_to_display_admin_window_messages();
221 
222  return true;
223 }
224 
226  std::string const & service_name
227  )
228 {
229  tools::msg_writer() << "Starting service";
230 
231  SERVICE_STATUS_PROCESS service_status = {};
232  DWORD unused = 0;
233 
234  service_handle p_manager{
235  OpenSCManager(
236  nullptr
237  , nullptr
238  , SC_MANAGER_CONNECT
239  )
240  , &::CloseServiceHandle
241  };
242  if (p_manager == nullptr)
243  {
244  tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
245  return false;
246  }
247 
248  service_handle p_service{
249  OpenService(
250  p_manager.get()
251  , service_name.c_str()
252  //, SERVICE_START | SERVICE_QUERY_STATUS
253  , SERVICE_START
254  )
255  , &::CloseServiceHandle
256  };
257  if (p_service == nullptr)
258  {
259  tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
260  return false;
261  }
262 
263  if (!StartService(
264  p_service.get()
265  , 0
266  , nullptr
267  ))
268  {
269  tools::fail_msg_writer() << "Service start request failed: " << get_last_error();
270  return false;
271  }
272 
273  tools::success_msg_writer() << "Service started";
274 
275  pause_to_display_admin_window_messages();
276 
277  return true;
278 }
279 
281  std::string const & service_name
282  )
283 {
284  tools::msg_writer() << "Stopping service";
285 
286  service_handle p_manager{
287  OpenSCManager(
288  nullptr
289  , nullptr
290  , SC_MANAGER_CONNECT
291  )
292  , &::CloseServiceHandle
293  };
294  if (p_manager == nullptr)
295  {
296  tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
297  return false;
298  }
299 
300  service_handle p_service{
301  OpenService(
302  p_manager.get()
303  , service_name.c_str()
304  , SERVICE_STOP | SERVICE_QUERY_STATUS
305  )
306  , &::CloseServiceHandle
307  };
308  if (p_service == nullptr)
309  {
310  tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
311  return false;
312  }
313 
314  SERVICE_STATUS status = {};
315  if (!ControlService(p_service.get(), SERVICE_CONTROL_STOP, &status))
316  {
317  tools::fail_msg_writer() << "Couldn't request service stop: " << get_last_error();
318  return false;
319  }
320 
321  tools::success_msg_writer() << "Service stopped";
322 
323  pause_to_display_admin_window_messages();
324 
325  return true;
326 }
327 
329  std::string const & service_name
330  )
331 {
332  service_handle p_manager{
333  OpenSCManager(
334  nullptr
335  , nullptr
336  , SC_MANAGER_CONNECT
337  )
338  , &::CloseServiceHandle
339  };
340  if (p_manager == nullptr)
341  {
342  tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
343  return false;
344  }
345 
346  service_handle p_service{
347  OpenService(
348  p_manager.get()
349  , service_name.c_str()
350  , SERVICE_QUERY_STATUS | DELETE
351  )
352  , &::CloseServiceHandle
353  };
354  if (p_service == nullptr)
355  {
356  tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
357  return false;
358  }
359 
360  SERVICE_STATUS status = {};
361  if (!DeleteService(p_service.get()))
362  {
363  tools::fail_msg_writer() << "Couldn't uninstall service: " << get_last_error();
364  return false;
365  }
366 
367  tools::success_msg_writer() << "Service uninstalled";
368 
369  pause_to_display_admin_window_messages();
370 
371  return true;
372 }
373 
374 } // namespace windows
::std::string string
Definition: gtest-port.h:1097
return true
bool check_admin(bool &result)
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
scoped_message_writer success_msg_writer(bool color=true)
scoped_message_writer fail_msg_writer()
bool start_service(std::string const &service_name)
bool ensure_admin(std::string const &arguments)
bool install_service(std::string const &service_name, std::string const &arguments)
bool stop_service(std::string const &service_name)
scoped_message_writer msg_writer(epee::console_colors color=epee::console_color_default)
bool uninstall_service(std::string const &service_name)