[CRT] Remove useless #undef abort from process.h
[reactos.git] / dll / win32 / advapi32 / service / scm.c
index c859d25..aee23c5 100644 (file)
@@ -5,13 +5,26 @@
  * PURPOSE:         Service control manager functions
  * PROGRAMMER:      Emanuele Aliberti
  *                  Eric Kohl
+ *                  Pierre Schweitzer
  */
 
 /* INCLUDES ******************************************************************/
 
 #include <advapi32.h>
-WINE_DEFAULT_DEBUG_CHANNEL(advapi_service);
+WINE_DEFAULT_DEBUG_CHANNEL(advapi);
 
+NTSTATUS
+WINAPI
+SystemFunction004(
+    const struct ustring *in,
+    const struct ustring *key,
+    struct ustring *out);
+
+NTSTATUS
+WINAPI
+SystemFunction028(
+    IN PVOID ContextHandle,
+    OUT LPBYTE SessionKey);
 
 /* FUNCTIONS *****************************************************************/
 
@@ -20,34 +33,35 @@ SVCCTL_HANDLEA_bind(SVCCTL_HANDLEA szMachineName)
 {
     handle_t hBinding = NULL;
     RPC_CSTR pszStringBinding;
-    RPC_STATUS Status;
+    RPC_STATUS status;
 
-    TRACE("SVCCTL_HANDLEA_bind()\n");
+    TRACE("SVCCTL_HANDLEA_bind(%s)\n",
+          debugstr_a(szMachineName));
 
-    Status = RpcStringBindingComposeA(NULL,
+    status = RpcStringBindingComposeA(NULL,
                                       (RPC_CSTR)"ncacn_np",
                                       (RPC_CSTR)szMachineName,
                                       (RPC_CSTR)"\\pipe\\ntsvcs",
                                       NULL,
                                       &pszStringBinding);
-    if (Status != RPC_S_OK)
+    if (status != RPC_S_OK)
     {
-        ERR("RpcStringBindingCompose returned 0x%x\n", Status);
+        ERR("RpcStringBindingCompose returned 0x%x\n", status);
         return NULL;
     }
 
     /* Set the binding handle that will be used to bind to the server. */
-    Status = RpcBindingFromStringBindingA(pszStringBinding,
+    status = RpcBindingFromStringBindingA(pszStringBinding,
                                           &hBinding);
-    if (Status != RPC_S_OK)
+    if (status != RPC_S_OK)
     {
-        ERR("RpcBindingFromStringBinding returned 0x%x\n", Status);
+        ERR("RpcBindingFromStringBinding returned 0x%x\n", status);
     }
 
-    Status = RpcStringFreeA(&pszStringBinding);
-    if (Status != RPC_S_OK)
+    status = RpcStringFreeA(&pszStringBinding);
+    if (status != RPC_S_OK)
     {
-        ERR("RpcStringFree returned 0x%x\n", Status);
+        ERR("RpcStringFree returned 0x%x\n", status);
     }
 
     return hBinding;
@@ -58,14 +72,15 @@ void __RPC_USER
 SVCCTL_HANDLEA_unbind(SVCCTL_HANDLEA szMachineName,
                       handle_t hBinding)
 {
-    RPC_STATUS Status;
+    RPC_STATUS status;
 
-    TRACE("SVCCTL_HANDLEA_unbind()\n");
+    TRACE("SVCCTL_HANDLEA_unbind(%s %p)\n",
+          debugstr_a(szMachineName), hBinding);
 
-    Status = RpcBindingFree(&hBinding);
-    if (Status != RPC_S_OK)
+    status = RpcBindingFree(&hBinding);
+    if (status != RPC_S_OK)
     {
-        ERR("RpcBindingFree returned 0x%x\n", Status);
+        ERR("RpcBindingFree returned 0x%x\n", status);
     }
 }
 
@@ -75,34 +90,35 @@ SVCCTL_HANDLEW_bind(SVCCTL_HANDLEW szMachineName)
 {
     handle_t hBinding = NULL;
     RPC_WSTR pszStringBinding;
-    RPC_STATUS Status;
+    RPC_STATUS status;
 
-    TRACE("SVCCTL_HANDLEW_bind()\n");
+    TRACE("SVCCTL_HANDLEW_bind(%s)\n",
+          debugstr_w(szMachineName));
 
-    Status = RpcStringBindingComposeW(NULL,
+    status = RpcStringBindingComposeW(NULL,
                                       L"ncacn_np",
                                       szMachineName,
                                       L"\\pipe\\ntsvcs",
                                       NULL,
                                       &pszStringBinding);
-    if (Status != RPC_S_OK)
+    if (status != RPC_S_OK)
     {
-        ERR("RpcStringBindingCompose returned 0x%x\n", Status);
+        ERR("RpcStringBindingCompose returned 0x%x\n", status);
         return NULL;
     }
 
     /* Set the binding handle that will be used to bind to the server. */
-    Status = RpcBindingFromStringBindingW(pszStringBinding,
+    status = RpcBindingFromStringBindingW(pszStringBinding,
                                           &hBinding);
-    if (Status != RPC_S_OK)
+    if (status != RPC_S_OK)
     {
-        ERR("RpcBindingFromStringBinding returned 0x%x\n", Status);
+        ERR("RpcBindingFromStringBinding returned 0x%x\n", status);
     }
 
-    Status = RpcStringFreeW(&pszStringBinding);
-    if (Status != RPC_S_OK)
+    status = RpcStringFreeW(&pszStringBinding);
+    if (status != RPC_S_OK)
     {
-        ERR("RpcStringFree returned 0x%x\n", Status);
+        ERR("RpcStringFree returned 0x%x\n", status);
     }
 
     return hBinding;
@@ -113,22 +129,25 @@ void __RPC_USER
 SVCCTL_HANDLEW_unbind(SVCCTL_HANDLEW szMachineName,
                       handle_t hBinding)
 {
-    RPC_STATUS Status;
+    RPC_STATUS status;
 
-    TRACE("SVCCTL_HANDLEW_unbind()\n");
+    TRACE("SVCCTL_HANDLEW_unbind(%s %p)\n",
+          debugstr_w(szMachineName), hBinding);
 
-    Status = RpcBindingFree(&hBinding);
-    if (Status != RPC_S_OK)
+    status = RpcBindingFree(&hBinding);
+    if (status != RPC_S_OK)
     {
-        ERR("RpcBindingFree returned 0x%x\n", Status);
+        ERR("RpcBindingFree returned 0x%x\n", status);
     }
 }
 
 
-/* HACK: because of a problem with rpcrt4, rpcserver is hacked to return 6 for ERROR_SERVICE_DOES_NOT_EXIST */
 DWORD
 ScmRpcStatusToWinError(RPC_STATUS Status)
 {
+    TRACE("ScmRpcStatusToWinError(%lx)\n",
+          Status);
+
     switch (Status)
     {
         case STATUS_ACCESS_VIOLATION:
@@ -149,6 +168,76 @@ ScmRpcStatusToWinError(RPC_STATUS Status)
 }
 
 
+static
+DWORD
+ScmEncryptPassword(
+    _In_ PVOID ContextHandle,
+    _In_ PCWSTR pClearTextPassword,
+    _Out_ PBYTE *pEncryptedPassword,
+    _Out_ PDWORD pEncryptedPasswordSize)
+{
+    struct ustring inData, keyData, outData;
+    BYTE SessionKey[16];
+    PBYTE pBuffer;
+    NTSTATUS Status;
+
+    /* Get the session key */
+    Status = SystemFunction028(ContextHandle,
+                               SessionKey);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SystemFunction028 failed (Status 0x%08lx)\n", Status);
+        return RtlNtStatusToDosError(Status);
+    }
+
+    inData.Length = (wcslen(pClearTextPassword) + 1) * sizeof(WCHAR);
+    inData.MaximumLength = inData.Length;
+    inData.Buffer = (unsigned char *)pClearTextPassword;
+
+    keyData.Length = sizeof(SessionKey);
+    keyData.MaximumLength = keyData.Length;
+    keyData.Buffer = SessionKey;
+
+    outData.Length = 0;
+    outData.MaximumLength = 0;
+    outData.Buffer = NULL;
+
+    /* Get the required buffer size */
+    Status = SystemFunction004(&inData,
+                               &keyData,
+                               &outData);
+    if (Status != STATUS_BUFFER_TOO_SMALL)
+    {
+        ERR("SystemFunction004 failed (Status 0x%08lx)\n", Status);
+        return RtlNtStatusToDosError(Status);
+    }
+
+    /* Allocate a buffer for the encrypted password */
+    pBuffer = HeapAlloc(GetProcessHeap(), 0, outData.Length);
+    if (pBuffer == NULL)
+        return ERROR_OUTOFMEMORY;
+
+    outData.MaximumLength = outData.Length;
+    outData.Buffer = pBuffer;
+
+    /* Encrypt the password */
+    Status = SystemFunction004(&inData,
+                               &keyData,
+                               &outData);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SystemFunction004 failed (Status 0x%08lx)\n", Status);
+        HeapFree(GetProcessHeap(), 0, pBuffer);
+        return RtlNtStatusToDosError(Status);
+    }
+
+    *pEncryptedPassword = outData.Buffer;
+    *pEncryptedPasswordSize = outData.Length;
+
+    return ERROR_SUCCESS;
+}
+
+
 /**********************************************************************
  *  ChangeServiceConfig2A
  *
@@ -162,7 +251,8 @@ ChangeServiceConfig2A(SC_HANDLE hService,
     SC_RPC_CONFIG_INFOA Info;
     DWORD dwError;
 
-    TRACE("ChangeServiceConfig2A()\n");
+    TRACE("ChangeServiceConfig2A(%p %lu %p)\n",
+          hService, dwInfoLevel, lpInfo);
 
     if (lpInfo == NULL) return TRUE;
 
@@ -219,7 +309,8 @@ ChangeServiceConfig2W(SC_HANDLE hService,
     SC_RPC_CONFIG_INFOW Info;
     DWORD dwError;
 
-    TRACE("ChangeServiceConfig2W()\n");
+    TRACE("ChangeServiceConfig2W(%p %lu %p)\n",
+          hService, dwInfoLevel, lpInfo);
 
     if (lpInfo == NULL) return TRUE;
 
@@ -285,11 +376,14 @@ ChangeServiceConfigA(SC_HANDLE hService,
     DWORD dwDependenciesLength = 0;
     SIZE_T cchLength;
     LPCSTR lpStr;
-    DWORD dwPasswordLength = 0;
+    DWORD dwPasswordSize = 0;
     LPWSTR lpPasswordW = NULL;
     LPBYTE lpEncryptedPassword = NULL;
 
-    TRACE("ChangeServiceConfigA()\n");
+    TRACE("ChangeServiceConfigA(%p %lu %lu %lu %s %s %p %s %s %s %s)\n",
+          hService, dwServiceType, dwStartType, dwErrorControl, debugstr_a(lpBinaryPathName),
+          debugstr_a(lpLoadOrderGroup), lpdwTagId, debugstr_a(lpDependencies),
+          debugstr_a(lpServiceStartName), debugstr_a(lpPassword), debugstr_a(lpDisplayName));
 
     /* Calculate the Dependencies length*/
     if (lpDependencies != NULL)
@@ -323,9 +417,13 @@ ChangeServiceConfigA(SC_HANDLE hService,
                             lpPasswordW,
                             (int)(strlen(lpPassword) + 1));
 
-        /* FIXME: Encrypt the password */
-        lpEncryptedPassword = (LPBYTE)lpPasswordW;
-        dwPasswordLength = (wcslen(lpPasswordW) + 1) * sizeof(WCHAR);
+        /* Encrypt the unicode password */
+        dwError = ScmEncryptPassword(hService,
+                                     lpPasswordW,
+                                     &lpEncryptedPassword,
+                                     &dwPasswordSize);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
     }
 
     RpcTryExcept
@@ -341,7 +439,7 @@ ChangeServiceConfigA(SC_HANDLE hService,
                                         dwDependenciesLength,
                                         (LPSTR)lpServiceStartName,
                                         lpEncryptedPassword,
-                                        dwPasswordLength,
+                                        dwPasswordSize,
                                         (LPSTR)lpDisplayName);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
@@ -350,9 +448,20 @@ ChangeServiceConfigA(SC_HANDLE hService,
     }
     RpcEndExcept;
 
+done:
     if (lpPasswordW != NULL)
+    {
+        /* Wipe and release the password buffers */
+        SecureZeroMemory(lpPasswordW, (wcslen(lpPasswordW) + 1) * sizeof(WCHAR));
         HeapFree(GetProcessHeap(), 0, lpPasswordW);
 
+        if (lpEncryptedPassword != NULL)
+        {
+            SecureZeroMemory(lpEncryptedPassword, dwPasswordSize);
+            HeapFree(GetProcessHeap(), 0, lpEncryptedPassword);
+        }
+    }
+
     if (dwError != ERROR_SUCCESS)
     {
         TRACE("RChangeServiceConfigA() failed (Error %lu)\n", dwError);
@@ -386,10 +495,13 @@ ChangeServiceConfigW(SC_HANDLE hService,
     DWORD dwDependenciesLength = 0;
     SIZE_T cchLength;
     LPCWSTR lpStr;
-    DWORD dwPasswordLength = 0;
+    DWORD dwPasswordSize = 0;
     LPBYTE lpEncryptedPassword = NULL;
 
-    TRACE("ChangeServiceConfigW()\n");
+    TRACE("ChangeServiceConfigW(%p %lu %lu %lu %s %s %p %s %s %s %s)\n",
+          hService, dwServiceType, dwStartType, dwErrorControl, debugstr_w(lpBinaryPathName),
+          debugstr_w(lpLoadOrderGroup), lpdwTagId, debugstr_w(lpDependencies),
+          debugstr_w(lpServiceStartName), debugstr_w(lpPassword), debugstr_w(lpDisplayName));
 
     /* Calculate the Dependencies length*/
     if (lpDependencies != NULL)
@@ -407,9 +519,15 @@ ChangeServiceConfigW(SC_HANDLE hService,
 
     if (lpPassword != NULL)
     {
-        /* FIXME: Encrypt the password */
-        lpEncryptedPassword = (LPBYTE)lpPassword;
-        dwPasswordLength = (wcslen(lpPassword) + 1) * sizeof(WCHAR);
+        dwError = ScmEncryptPassword(hService,
+                                     lpPassword,
+                                     &lpEncryptedPassword,
+                                     &dwPasswordSize);
+        if (dwError != ERROR_SUCCESS)
+        {
+            ERR("ScmEncryptPassword failed (Error %lu)\n", dwError);
+            goto done;
+        }
     }
 
     RpcTryExcept
@@ -425,7 +543,7 @@ ChangeServiceConfigW(SC_HANDLE hService,
                                         dwDependenciesLength,
                                         (LPWSTR)lpServiceStartName,
                                         lpEncryptedPassword,
-                                        dwPasswordLength,
+                                        dwPasswordSize,
                                         (LPWSTR)lpDisplayName);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
@@ -434,6 +552,14 @@ ChangeServiceConfigW(SC_HANDLE hService,
     }
     RpcEndExcept;
 
+done:
+    if (lpEncryptedPassword != NULL)
+    {
+        /* Wipe and release the password buffer */
+        SecureZeroMemory(lpEncryptedPassword, dwPasswordSize);
+        HeapFree(GetProcessHeap(), 0, lpEncryptedPassword);
+    }
+
     if (dwError != ERROR_SUCCESS)
     {
         TRACE("RChangeServiceConfigW() failed (Error %lu)\n", dwError);
@@ -570,12 +696,15 @@ CreateServiceA(SC_HANDLE hSCManager,
     DWORD dwError;
     SIZE_T cchLength;
     LPCSTR lpStr;
-    DWORD dwPasswordLength = 0;
+    DWORD dwPasswordSize = 0;
     LPWSTR lpPasswordW = NULL;
     LPBYTE lpEncryptedPassword = NULL;
 
-    TRACE("CreateServiceA(%p %s %s)\n",
-          hSCManager, debugstr_a(lpServiceName), debugstr_a(lpDisplayName));
+    TRACE("CreateServiceA(%p %s %s %lx %lu %lu %lu %s %s %p %s %s %s)\n",
+          hSCManager, debugstr_a(lpServiceName), debugstr_a(lpDisplayName),
+          dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
+          debugstr_a(lpBinaryPathName), debugstr_a(lpLoadOrderGroup), lpdwTagId,
+          debugstr_a(lpDependencies), debugstr_a(lpServiceStartName), debugstr_a(lpPassword));
 
     if (!hSCManager)
     {
@@ -615,9 +744,13 @@ CreateServiceA(SC_HANDLE hSCManager,
                             lpPasswordW,
                             (int)(strlen(lpPassword) + 1));
 
-        /* FIXME: Encrypt the password */
-        lpEncryptedPassword = (LPBYTE)lpPasswordW;
-        dwPasswordLength = (wcslen(lpPasswordW) + 1) * sizeof(WCHAR);
+        /* Encrypt the password */
+        dwError = ScmEncryptPassword(hSCManager,
+                                     lpPasswordW,
+                                     &lpEncryptedPassword,
+                                     &dwPasswordSize);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
     }
 
     RpcTryExcept
@@ -636,7 +769,7 @@ CreateServiceA(SC_HANDLE hSCManager,
                                   dwDependenciesLength,
                                   (LPSTR)lpServiceStartName,
                                   lpEncryptedPassword,
-                                  dwPasswordLength,
+                                  dwPasswordSize,
                                   (SC_RPC_HANDLE *)&hService);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
@@ -645,9 +778,20 @@ CreateServiceA(SC_HANDLE hSCManager,
     }
     RpcEndExcept;
 
+done:
     if (lpPasswordW != NULL)
+    {
+        /* Wipe and release the password buffers */
+        SecureZeroMemory(lpPasswordW, (wcslen(lpPasswordW) + 1) * sizeof(WCHAR));
         HeapFree(GetProcessHeap(), 0, lpPasswordW);
 
+        if (lpEncryptedPassword != NULL)
+        {
+            SecureZeroMemory(lpEncryptedPassword, dwPasswordSize);
+            HeapFree(GetProcessHeap(), 0, lpEncryptedPassword);
+        }
+    }
+
     SetLastError(dwError);
     if (dwError != ERROR_SUCCESS)
     {
@@ -684,11 +828,14 @@ CreateServiceW(SC_HANDLE hSCManager,
     DWORD dwError;
     SIZE_T cchLength;
     LPCWSTR lpStr;
-    DWORD dwPasswordLength = 0;
+    DWORD dwPasswordSize = 0;
     LPBYTE lpEncryptedPassword = NULL;
 
-    TRACE("CreateServiceW(%p %s %s)\n",
-          hSCManager, debugstr_w(lpServiceName), debugstr_w(lpDisplayName));
+    TRACE("CreateServiceW(%p %s %s %lx %lu %lu %lu %s %s %p %s %s %s)\n",
+          hSCManager, debugstr_w(lpServiceName), debugstr_w(lpDisplayName),
+          dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
+          debugstr_w(lpBinaryPathName), debugstr_w(lpLoadOrderGroup), lpdwTagId,
+          debugstr_w(lpDependencies), debugstr_w(lpServiceStartName), debugstr_w(lpPassword));
 
     if (!hSCManager)
     {
@@ -712,9 +859,13 @@ CreateServiceW(SC_HANDLE hSCManager,
 
     if (lpPassword != NULL)
     {
-        /* FIXME: Encrypt the password */
-        lpEncryptedPassword = (LPBYTE)lpPassword;
-        dwPasswordLength = (wcslen(lpPassword) + 1) * sizeof(WCHAR);
+        /* Encrypt the password */
+        dwError = ScmEncryptPassword(hSCManager,
+                                     lpPassword,
+                                     &lpEncryptedPassword,
+                                     &dwPasswordSize);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
     }
 
     RpcTryExcept
@@ -733,7 +884,7 @@ CreateServiceW(SC_HANDLE hSCManager,
                                   dwDependenciesLength,
                                   lpServiceStartName,
                                   lpEncryptedPassword,
-                                  dwPasswordLength,
+                                  dwPasswordSize,
                                   (SC_RPC_HANDLE *)&hService);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
@@ -742,6 +893,14 @@ CreateServiceW(SC_HANDLE hSCManager,
     }
     RpcEndExcept;
 
+done:
+    if (lpEncryptedPassword != NULL)
+    {
+        /* Wipe and release the password buffers */
+        SecureZeroMemory(lpEncryptedPassword, dwPasswordSize);
+        HeapFree(GetProcessHeap(), 0, lpEncryptedPassword);
+    }
+
     SetLastError(dwError);
     if (dwError != ERROR_SUCCESS)
     {
@@ -806,7 +965,9 @@ EnumDependentServicesA(SC_HANDLE hService,
     DWORD dwError;
     DWORD dwCount;
 
-    TRACE("EnumDependentServicesA()\n");
+    TRACE("EnumDependentServicesA(%p %lu %p %lu %p %p)\n",
+          hService, dwServiceState, lpServices, cbBufSize,
+          pcbBytesNeeded, lpServicesReturned);
 
     if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUSA))
     {
@@ -882,7 +1043,9 @@ EnumDependentServicesW(SC_HANDLE hService,
     DWORD dwError;
     DWORD dwCount;
 
-    TRACE("EnumDependentServicesW()\n");
+    TRACE("EnumDependentServicesW(%p %lu %p %lu %p %p)\n",
+          hService, dwServiceState, lpServices, cbBufSize,
+          pcbBytesNeeded, lpServicesReturned);
 
     if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUSW))
     {
@@ -961,7 +1124,10 @@ EnumServiceGroupW(SC_HANDLE hSCManager,
     DWORD dwError;
     DWORD dwCount;
 
-    TRACE("EnumServiceGroupW()\n");
+    TRACE("EnumServiceGroupW(%p %lu %lu %p %lu %p %p %p %s)\n",
+          hSCManager, dwServiceType, dwServiceState, lpServices,
+          cbBufSize, pcbBytesNeeded, lpServicesReturned,
+          lpResumeHandle, debugstr_w(lpGroup));
 
     if (!hSCManager)
     {
@@ -1068,7 +1234,9 @@ EnumServicesStatusA(SC_HANDLE hSCManager,
     DWORD dwError;
     DWORD dwCount;
 
-    TRACE("EnumServicesStatusA()\n");
+    TRACE("EnumServicesStatusA(%p %lu %lu %p %lu %p %p %p)\n",
+          hSCManager, dwServiceType, dwServiceState, lpServices,
+          cbBufSize, pcbBytesNeeded, lpServicesReturned, lpResumeHandle);
 
     if (!hSCManager)
     {
@@ -1160,7 +1328,9 @@ EnumServicesStatusW(SC_HANDLE hSCManager,
     DWORD dwError;
     DWORD dwCount;
 
-    TRACE("EnumServicesStatusW()\n");
+    TRACE("EnumServicesStatusW(%p %lu %lu %p %lu %p %p %p)\n",
+          hSCManager, dwServiceType, dwServiceState, lpServices,
+          cbBufSize, pcbBytesNeeded, lpServicesReturned, lpResumeHandle);
 
     if (!hSCManager)
     {
@@ -1254,7 +1424,10 @@ EnumServicesStatusExA(SC_HANDLE hSCManager,
     DWORD dwError;
     DWORD dwCount;
 
-    TRACE("EnumServicesStatusExA()\n");
+    TRACE("EnumServicesStatusExA(%p %lu %lu %p %lu %p %p %p %s)\n",
+          hSCManager, dwServiceType, dwServiceState, lpServices,
+          cbBufSize, pcbBytesNeeded, lpServicesReturned, lpResumeHandle,
+          debugstr_a(pszGroupName));
 
     if (InfoLevel != SC_ENUM_PROCESS_INFO)
     {
@@ -1359,7 +1532,10 @@ EnumServicesStatusExW(SC_HANDLE hSCManager,
     DWORD dwError;
     DWORD dwCount;
 
-    TRACE("EnumServicesStatusExW()\n");
+    TRACE("EnumServicesStatusExW(%p %lu %lu %p %lu %p %p %p %s)\n",
+          hSCManager, dwServiceType, dwServiceState, lpServices,
+          cbBufSize, pcbBytesNeeded, lpServicesReturned, lpResumeHandle,
+          debugstr_w(pszGroupName));
 
     if (InfoLevel != SC_ENUM_PROCESS_INFO)
     {
@@ -1523,6 +1699,11 @@ GetServiceDisplayNameW(SC_HANDLE hSCManager,
         return FALSE;
     }
 
+    /*
+     * NOTE: A size of 1 character would be enough, but tests show that
+     * Windows returns 2 characters instead, certainly due to a WCHAR/bytes
+     * mismatch in their code.
+     */
     if (!lpDisplayName || *lpcchBuffer < sizeof(WCHAR))
     {
         lpNameBuffer = szEmptyName;
@@ -1600,6 +1781,7 @@ GetServiceKeyNameA(SC_HANDLE hSCManager,
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
+        /* HACK: because of a problem with rpcrt4, rpcserver is hacked to return 6 for ERROR_SERVICE_DOES_NOT_EXIST */
         dwError = ScmRpcStatusToWinError(RpcExceptionCode());
     }
     RpcEndExcept;
@@ -1639,6 +1821,11 @@ GetServiceKeyNameW(SC_HANDLE hSCManager,
         return FALSE;
     }
 
+    /*
+     * NOTE: A size of 1 character would be enough, but tests show that
+     * Windows returns 2 characters instead, certainly due to a WCHAR/bytes
+     * mismatch in their code.
+     */
     if (!lpServiceName || *lpcchBuffer < sizeof(WCHAR))
     {
         lpNameBuffer = szEmptyName;
@@ -1710,6 +1897,58 @@ I_ScGetCurrentGroupStateW(SC_HANDLE hSCManager,
 }
 
 
+/**********************************************************************
+ *  I_ScValidatePnpService
+ *
+ * Undocumented
+ *
+ * @implemented
+ */
+DWORD
+WINAPI
+I_ScValidatePnpService(
+    _In_ LPCWSTR pszMachineName,
+    _In_ LPCWSTR pszServiceName,
+    _Out_ SERVICE_STATUS_HANDLE *phServiceStatus)
+{
+    SC_RPC_HANDLE hSCManager = NULL;
+    SERVICE_STATUS_HANDLE hServiceStatus = NULL;
+    DWORD dwError;
+
+    TRACE("I_ScValidatePnpService(%S %S %p)\n",
+         pszMachineName, pszServiceName, phServiceStatus);
+
+    hSCManager = OpenSCManagerW(pszMachineName,
+                                SERVICES_ACTIVE_DATABASEW,
+                                SC_MANAGER_CONNECT);
+    if (hSCManager == NULL)
+    {
+        dwError = GetLastError();
+        goto done;
+    }
+
+    RpcTryExcept
+    {
+        dwError = RI_ScValidatePnPService(hSCManager,
+                                          (LPWSTR)pszServiceName,
+                                          (RPC_SERVICE_STATUS_HANDLE *)&hServiceStatus);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
+    }
+    RpcEndExcept
+
+    *phServiceStatus = hServiceStatus;
+
+done:
+    if (hSCManager != NULL)
+        CloseServiceHandle(hSCManager);
+
+    return dwError;
+}
+
+
 /**********************************************************************
  *  LockServiceDatabase
  *
@@ -1759,11 +1998,13 @@ WaitForSCManager(VOID)
     hEvent = OpenEventW(SYNCHRONIZE, FALSE, SCM_START_EVENT);
     if (hEvent == NULL)
     {
-        if (GetLastError() != ERROR_FILE_NOT_FOUND) return;
+        if (GetLastError() != ERROR_FILE_NOT_FOUND)
+            return;
 
         /* Try to create a new event */
         hEvent = CreateEventW(NULL, TRUE, FALSE, SCM_START_EVENT);
-        if (hEvent == NULL) return;
+        if (hEvent == NULL)
+            return;
     }
 
     /* Wait for 3 minutes */
@@ -1805,10 +2046,10 @@ OpenSCManagerA(LPCSTR lpMachineName,
     }
     RpcEndExcept;
 
+    SetLastError(dwError);
     if (dwError != ERROR_SUCCESS)
     {
         TRACE("ROpenSCManagerA() failed (Error %lu)\n", dwError);
-        SetLastError(dwError);
         return NULL;
     }
 
@@ -1849,10 +2090,10 @@ OpenSCManagerW(LPCWSTR lpMachineName,
     }
     RpcEndExcept;
 
+    SetLastError(dwError);
     if (dwError != ERROR_SUCCESS)
     {
         TRACE("ROpenSCManagerW() failed (Error %lu)\n", dwError);
-        SetLastError(dwError);
         return NULL;
     }
 
@@ -2839,4 +3080,135 @@ NotifyBootConfigStatus(BOOL BootAcceptable)
     return TRUE;
 }
 
+DWORD
+I_ScQueryServiceTagInfo(PVOID Unused,
+                        TAG_INFO_LEVEL dwInfoLevel,
+                        PTAG_INFO_NAME_FROM_TAG InOutParams)
+{
+    SC_HANDLE hScm;
+    DWORD dwError;
+    PTAG_INFO_NAME_FROM_TAG_IN_PARAMS InParams;
+    PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS OutParams;
+    LPWSTR lpszName;
+
+    /* We only support one class */
+    if (dwInfoLevel != TagInfoLevelNameFromTag)
+    {
+        return ERROR_RETRY;
+    }
+
+    /* Validate input structure */
+    if (InOutParams->InParams.dwPid == 0 || InOutParams->InParams.dwTag == 0)
+    {
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /* Validate output structure */
+    if (InOutParams->OutParams.pszName != NULL)
+    {
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /* Open service manager */
+    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
+    if (hScm == NULL)
+    {
+        return GetLastError();
+    }
+
+    /* Setup call parameters */
+    InParams = &InOutParams->InParams;
+    OutParams = NULL;
+
+    /* Call SCM to query tag information */
+    RpcTryExcept
+    {
+        dwError = RI_ScQueryServiceTagInfo(hScm, TagInfoLevelNameFromTag, &InParams, &OutParams);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
+    }
+    RpcEndExcept;
+
+    /* Quit if not a success */
+    if (dwError != ERROR_SUCCESS)
+    {
+        goto Cleanup;
+    }
+
+    /* OutParams must be set now and we must have a name */
+    if (OutParams == NULL ||
+        OutParams->pszName == NULL)
+    {
+        dwError = ERROR_INVALID_DATA;
+        goto Cleanup;
+    }
+
+    /* Copy back what SCM returned */
+    lpszName = LocalAlloc(LPTR,
+                          sizeof(WCHAR) * wcslen(OutParams->pszName) + sizeof(UNICODE_NULL));
+    if (lpszName == NULL)
+    {
+        dwError = GetLastError();
+        goto Cleanup;
+    }
+
+    wcscpy(lpszName, OutParams->pszName);
+    InOutParams->OutParams.pszName = lpszName;
+    InOutParams->OutParams.TagType = OutParams->TagType;
+
+Cleanup:
+    CloseServiceHandle(hScm);
+
+    /* Free memory allocated by SCM */
+    if (OutParams != NULL)
+    {
+        if (OutParams->pszName != NULL)
+        {
+            midl_user_free(OutParams->pszName);
+        }
+
+        midl_user_free(OutParams);
+    }
+
+    return dwError;
+}
+
+/**********************************************************************
+ *  I_QueryTagInformation
+ *
+ * @implemented
+ */
+DWORD WINAPI
+I_QueryTagInformation(PVOID Unused,
+                      TAG_INFO_LEVEL dwInfoLevel,
+                      PTAG_INFO_NAME_FROM_TAG InOutParams)
+{
+    /*
+     * We only support one information class and it
+     * needs parameters
+     */
+    if (dwInfoLevel != TagInfoLevelNameFromTag ||
+        InOutParams == NULL)
+    {
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /* Validate input structure */
+    if (InOutParams->InParams.dwPid == 0 || InOutParams->InParams.dwTag == 0)
+    {
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /* Validate output structure */
+    if (InOutParams->OutParams.pszName != NULL)
+    {
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /* Call internal function for the RPC call */
+    return I_ScQueryServiceTagInfo(Unused, TagInfoLevelNameFromTag, InOutParams);
+}
+
 /* EOF */