services: Move ChangeServiceConfigW implementation from advapi32.dll to services...
[wine/wine64.git] / programs / services / rpc.c
blob5c747692b2c578a759e13e66cc4615a935f70e30
1 /*
2 * Services.exe - RPC functions
4 * Copyright 2007 Google (Mikolaj Zalewski)
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define WIN32_LEAN_AND_MEAN
23 #include <stdarg.h>
24 #include <windows.h>
25 #include <winternl.h>
26 #include <winsvc.h>
27 #include <ntsecapi.h>
28 #include <rpc.h>
30 #include "wine/list.h"
31 #include "wine/unicode.h"
32 #include "wine/debug.h"
34 #include "services.h"
35 #include "svcctl.h"
37 extern HANDLE __wine_make_process_system(void);
39 WINE_DEFAULT_DEBUG_CHANNEL(service);
41 static CRITICAL_SECTION g_handle_table_cs;
42 static CRITICAL_SECTION_DEBUG g_handle_table_cs_debug =
44 0, 0, &g_handle_table_cs,
45 { &g_handle_table_cs_debug.ProcessLocksList,
46 &g_handle_table_cs_debug.ProcessLocksList },
47 0, 0, { (DWORD_PTR)(__FILE__ ": g_handle_table_cs") }
49 static CRITICAL_SECTION g_handle_table_cs = { &g_handle_table_cs_debug, -1, 0, 0, 0, 0 };
51 static const GENERIC_MAPPING g_scm_generic =
53 (STANDARD_RIGHTS_READ | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS),
54 (STANDARD_RIGHTS_WRITE | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_MODIFY_BOOT_CONFIG),
55 (STANDARD_RIGHTS_EXECUTE | SC_MANAGER_CONNECT | SC_MANAGER_LOCK),
56 SC_MANAGER_ALL_ACCESS
59 static const GENERIC_MAPPING g_svc_generic =
61 (STANDARD_RIGHTS_READ | SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS),
62 (STANDARD_RIGHTS_WRITE | SERVICE_CHANGE_CONFIG),
63 (STANDARD_RIGHTS_EXECUTE | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_USER_DEFINED_CONTROL),
64 SERVICE_ALL_ACCESS
67 typedef enum
69 SC_HTYPE_DONT_CARE = 0,
70 SC_HTYPE_MANAGER,
71 SC_HTYPE_SERVICE
72 } SC_HANDLE_TYPE;
74 struct sc_handle
76 SC_HANDLE_TYPE type;
77 DWORD access;
80 struct sc_manager /* service control manager handle */
82 struct sc_handle hdr;
85 struct sc_service /* service handle */
87 struct sc_handle hdr;
88 struct service_entry *service_entry;
91 /* Check if the given handle is of the required type and allows the requested access. */
92 static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD needed_access, struct sc_handle **out_hdr)
94 struct sc_handle *hdr = (struct sc_handle *)handle;
96 if (type != SC_HTYPE_DONT_CARE && hdr->type != type)
98 WINE_ERR("Handle is of an invalid type (%d, %d)\n", hdr->type, type);
99 return ERROR_INVALID_HANDLE;
102 if ((needed_access & hdr->access) != needed_access)
104 WINE_ERR("Access denied - handle created with access %x, needed %x\n", hdr->access, needed_access);
105 return ERROR_ACCESS_DENIED;
108 *out_hdr = hdr;
109 return ERROR_SUCCESS;
112 static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager **manager)
114 struct sc_handle *hdr;
115 DWORD err = validate_context_handle(handle, SC_HTYPE_MANAGER, needed_access, &hdr);
116 if (err == ERROR_SUCCESS)
117 *manager = (struct sc_manager *)hdr;
118 return err;
121 static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service **service)
123 struct sc_handle *hdr;
124 DWORD err = validate_context_handle(handle, SC_HTYPE_SERVICE, needed_access, &hdr);
125 if (err == ERROR_SUCCESS)
126 *service = (struct sc_service *)hdr;
127 return err;
130 DWORD svcctl_OpenSCManagerW(
131 MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
132 LPCWSTR DatabaseName,
133 DWORD dwAccessMask,
134 SC_RPC_HANDLE *handle)
136 struct sc_manager *manager;
138 WINE_TRACE("(%s, %s, %x)\n", wine_dbgstr_w(MachineName), wine_dbgstr_w(DatabaseName), dwAccessMask);
140 if (DatabaseName != NULL && DatabaseName[0])
142 if (strcmpW(DatabaseName, SERVICES_FAILED_DATABASEW) == 0)
143 return ERROR_DATABASE_DOES_NOT_EXIST;
144 if (strcmpW(DatabaseName, SERVICES_ACTIVE_DATABASEW) != 0)
145 return ERROR_INVALID_NAME;
148 if (!(manager = HeapAlloc(GetProcessHeap(), 0, sizeof(*manager))))
149 return ERROR_NOT_ENOUGH_SERVER_MEMORY;
151 manager->hdr.type = SC_HTYPE_MANAGER;
153 if (dwAccessMask & MAXIMUM_ALLOWED)
154 dwAccessMask |= SC_MANAGER_ALL_ACCESS;
155 manager->hdr.access = dwAccessMask;
156 RtlMapGenericMask(&manager->hdr.access, &g_scm_generic);
157 *handle = &manager->hdr;
159 return ERROR_SUCCESS;
162 static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
164 struct sc_handle *hdr = (struct sc_handle *)handle;
165 switch (hdr->type)
167 case SC_HTYPE_MANAGER:
169 struct sc_manager *manager = (struct sc_manager *)hdr;
170 HeapFree(GetProcessHeap(), 0, manager);
171 break;
173 case SC_HTYPE_SERVICE:
175 struct sc_service *service = (struct sc_service *)hdr;
176 release_service(service->service_entry);
177 HeapFree(GetProcessHeap(), 0, service);
178 break;
180 default:
181 WINE_ERR("invalid handle type %d\n", hdr->type);
182 RpcRaiseException(ERROR_INVALID_HANDLE);
186 static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
188 struct sc_service *service;
190 if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
192 release_service(entry);
193 return ERROR_NOT_ENOUGH_SERVER_MEMORY;
196 service->hdr.type = SC_HTYPE_SERVICE;
197 service->hdr.access = dwDesiredAccess;
198 RtlMapGenericMask(&service->hdr.access, &g_svc_generic);
199 service->service_entry = entry;
200 if (dwDesiredAccess & MAXIMUM_ALLOWED)
201 dwDesiredAccess |= SERVICE_ALL_ACCESS;
203 *phService = &service->hdr;
204 return ERROR_SUCCESS;
207 DWORD svcctl_OpenServiceW(
208 SC_RPC_HANDLE hSCManager,
209 LPCWSTR lpServiceName,
210 DWORD dwDesiredAccess,
211 SC_RPC_HANDLE *phService)
213 struct sc_manager *manager;
214 struct service_entry *entry;
215 DWORD err;
217 WINE_TRACE("(%s, 0x%x)\n", wine_dbgstr_w(lpServiceName), dwDesiredAccess);
219 if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
220 return err;
221 if (!validate_service_name(lpServiceName))
222 return ERROR_INVALID_NAME;
224 lock_services();
225 entry = find_service(lpServiceName);
226 if (entry != NULL)
227 entry->ref_count++;
228 unlock_services();
230 if (entry == NULL)
231 return ERROR_SERVICE_DOES_NOT_EXIST;
233 return create_handle_for_service(entry, dwDesiredAccess, phService);
236 DWORD svcctl_CreateServiceW(
237 SC_RPC_HANDLE hSCManager,
238 LPCWSTR lpServiceName,
239 LPCWSTR lpDisplayName,
240 DWORD dwDesiredAccess,
241 DWORD dwServiceType,
242 DWORD dwStartType,
243 DWORD dwErrorControl,
244 LPCWSTR lpBinaryPathName,
245 LPCWSTR lpLoadOrderGroup,
246 DWORD *lpdwTagId,
247 const BYTE *lpDependencies,
248 DWORD dwDependenciesSize,
249 LPCWSTR lpServiceStartName,
250 const BYTE *lpPassword,
251 DWORD dwPasswordSize,
252 SC_RPC_HANDLE *phService)
254 struct sc_manager *manager;
255 struct service_entry *entry;
256 DWORD err;
258 WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
260 if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE, &manager)) != ERROR_SUCCESS)
261 return err;
263 if (!validate_service_name(lpServiceName))
264 return ERROR_INVALID_NAME;
265 if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize) || !lpServiceName[0] || !lpBinaryPathName[0])
266 return ERROR_INVALID_PARAMETER;
268 if (lpPassword)
269 WINE_FIXME("Don't know how to add a password\n"); /* I always get ERROR_GEN_FAILURE */
270 if (lpDependencies)
271 WINE_FIXME("Dependencies not supported yet\n");
273 entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entry));
274 entry->name = strdupW(lpServiceName);
275 entry->config.dwServiceType = dwServiceType;
276 entry->config.dwStartType = dwStartType;
277 entry->config.dwErrorControl = dwErrorControl;
278 entry->config.lpBinaryPathName = strdupW(lpBinaryPathName);
279 entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
280 entry->config.lpServiceStartName = strdupW(lpServiceStartName);
281 entry->config.lpDisplayName = strdupW(lpDisplayName);
283 if (lpdwTagId) /* TODO: in most situations a non-NULL tagid will generate a ERROR_INVALID_PARAMETER */
284 entry->config.dwTagId = *lpdwTagId;
285 else
286 entry->config.dwTagId = 0;
288 /* other fields NULL*/
290 if (!validate_service_config(entry))
292 WINE_ERR("Invalid data while trying to create service\n");
293 free_service_entry(entry);
294 return ERROR_INVALID_PARAMETER;
297 lock_services();
299 if (find_service(lpServiceName))
301 unlock_services();
302 free_service_entry(entry);
303 return ERROR_SERVICE_EXISTS;
306 if (find_service_by_displayname(get_display_name(entry)))
308 unlock_services();
309 free_service_entry(entry);
310 return ERROR_DUPLICATE_SERVICE_NAME;
313 err = add_service(entry);
314 if (err != ERROR_SUCCESS)
316 unlock_services();
317 free_service_entry(entry);
318 return err;
320 unlock_services();
322 return create_handle_for_service(entry, dwDesiredAccess, phService);
325 DWORD svcctl_DeleteService(
326 SC_RPC_HANDLE hService)
328 struct sc_service *service;
329 DWORD err;
331 if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
332 return err;
334 lock_services();
336 if (!is_marked_for_delete(service->service_entry))
337 err = remove_service(service->service_entry);
338 else
339 err = ERROR_SERVICE_MARKED_FOR_DELETE;
341 unlock_services();
343 return err;
346 DWORD svcctl_QueryServiceConfigW(
347 SC_RPC_HANDLE hService,
348 QUERY_SERVICE_CONFIGW *config)
350 struct sc_service *service;
351 DWORD err;
353 WINE_TRACE("(%p)\n", config);
355 if ((err = validate_service_handle(hService, SERVICE_QUERY_CONFIG, &service)) != 0)
356 return err;
358 lock_services();
359 config->dwServiceType = service->service_entry->config.dwServiceType;
360 config->dwStartType = service->service_entry->config.dwStartType;
361 config->dwErrorControl = service->service_entry->config.dwErrorControl;
362 config->lpBinaryPathName = strdupW(service->service_entry->config.lpBinaryPathName);
363 config->lpLoadOrderGroup = strdupW(service->service_entry->config.lpLoadOrderGroup);
364 config->dwTagId = service->service_entry->config.dwTagId;
365 config->lpDependencies = NULL; /* TODO */
366 config->lpServiceStartName = strdupW(service->service_entry->config.lpServiceStartName);
367 config->lpDisplayName = strdupW(service->service_entry->config.lpDisplayName);
368 unlock_services();
370 return ERROR_SUCCESS;
373 DWORD svcctl_ChangeServiceConfigW(
374 SC_RPC_HANDLE hService,
375 DWORD dwServiceType,
376 DWORD dwStartType,
377 DWORD dwErrorControl,
378 LPCWSTR lpBinaryPathName,
379 LPCWSTR lpLoadOrderGroup,
380 DWORD *lpdwTagId,
381 const BYTE *lpDependencies,
382 DWORD dwDependenciesSize,
383 LPCWSTR lpServiceStartName,
384 const BYTE *lpPassword,
385 DWORD dwPasswordSize,
386 LPCWSTR lpDisplayName)
388 struct service_entry new_entry;
389 struct sc_service *service;
390 DWORD err;
392 WINE_TRACE("\n");
394 if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
395 return err;
397 if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize))
398 return ERROR_INVALID_PARAMETER;
400 /* first check if the new configuration is correct */
401 lock_services();
403 if (is_marked_for_delete(service->service_entry))
405 unlock_services();
406 return ERROR_SERVICE_MARKED_FOR_DELETE;
409 if (lpDisplayName != NULL && find_service_by_displayname(lpDisplayName))
411 unlock_services();
412 return ERROR_DUPLICATE_SERVICE_NAME;
415 new_entry = *service->service_entry;
417 if (dwServiceType != SERVICE_NO_CHANGE)
418 new_entry.config.dwServiceType = dwServiceType;
420 if (dwStartType != SERVICE_NO_CHANGE)
421 new_entry.config.dwStartType = dwStartType;
423 if (dwErrorControl != SERVICE_NO_CHANGE)
424 new_entry.config.dwErrorControl = dwErrorControl;
426 if (lpBinaryPathName != NULL)
427 new_entry.config.lpBinaryPathName = (LPWSTR)lpBinaryPathName;
429 if (lpLoadOrderGroup != NULL)
430 new_entry.config.lpLoadOrderGroup = (LPWSTR)lpLoadOrderGroup;
432 if (lpdwTagId != NULL)
433 WINE_FIXME("Changing tag id not supported\n");
435 if (lpDependencies != NULL)
436 WINE_FIXME("Chainging dependencies not supported\n");
438 if (lpServiceStartName != NULL)
439 new_entry.config.lpServiceStartName = (LPWSTR)lpServiceStartName;
441 if (lpPassword != NULL)
442 WINE_FIXME("Setting password not supported\n");
444 if (lpDisplayName != NULL)
445 new_entry.config.lpDisplayName = (LPWSTR)lpDisplayName;
447 if (!validate_service_config(&new_entry))
449 WINE_ERR("The configuration after the change wouldn't be valid");
450 unlock_services();
451 return ERROR_INVALID_PARAMETER;
454 /* configuration OK. The strings needs to be duplicated */
455 if (lpBinaryPathName != NULL)
457 HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpBinaryPathName);
458 new_entry.config.lpBinaryPathName = strdupW(lpBinaryPathName);
461 if (lpLoadOrderGroup != NULL)
463 HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpLoadOrderGroup);
464 new_entry.config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
467 if (lpServiceStartName != NULL)
469 HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpServiceStartName);
470 new_entry.config.lpServiceStartName = strdupW(lpServiceStartName);
473 if (lpDisplayName != NULL)
475 HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpDisplayName);
476 new_entry.config.lpDisplayName = strdupW(lpDisplayName);
479 *service->service_entry = new_entry;
480 save_service_config(service->service_entry);
481 unlock_services();
483 return ERROR_SUCCESS;
486 DWORD svcctl_CloseServiceHandle(
487 SC_RPC_HANDLE *handle)
489 WINE_TRACE("(&%p)\n", *handle);
491 SC_RPC_HANDLE_destroy(*handle);
492 *handle = NULL;
494 return ERROR_SUCCESS;
497 DWORD RPC_MainLoop(void)
499 WCHAR transport[] = SVCCTL_TRANSPORT;
500 WCHAR endpoint[] = SVCCTL_ENDPOINT;
501 HANDLE hSleepHandle;
502 DWORD err;
504 if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS)
506 WINE_ERR("RpcServerUseProtseq failed with error %u\n", err);
507 return err;
510 if ((err = RpcServerRegisterIf(svcctl_v2_0_s_ifspec, 0, 0)) != ERROR_SUCCESS)
512 WINE_ERR("RpcServerRegisterIf failed with error %u", err);
513 return err;
516 if ((err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE)) != ERROR_SUCCESS)
518 WINE_ERR("RpcServerListen failed with error %u\n", err);
519 return err;
522 WINE_TRACE("Entered main loop\n");
523 hSleepHandle = __wine_make_process_system();
524 SetEvent(g_hStartedEvent);
527 err = WaitForSingleObjectEx(hSleepHandle, INFINITE, TRUE);
528 WINE_TRACE("Wait returned %d\n", err);
529 } while (err != WAIT_OBJECT_0);
531 WINE_TRACE("Object signaled - wine shutdown\n");
532 return ERROR_SUCCESS;
535 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle)
537 SC_RPC_HANDLE_destroy(handle);
540 void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len)
542 return HeapAlloc(GetProcessHeap(), 0, len);
545 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
547 HeapFree(GetProcessHeap(), 0, ptr);