Skip to content

Instantly share code, notes, and snippets.

@TheVice
Last active February 24, 2019 21:53
Show Gist options
  • Save TheVice/0def38c54d74760b655f0d9d8aaabc6a to your computer and use it in GitHub Desktop.
Save TheVice/0def38c54d74760b655f0d9d8aaabc6a to your computer and use it in GitHub Desktop.
Tool allow to install service for specific user. If it do not exists it will be created as regular and required right for run service will be granted. Password will be generated that meet policy at Windows Server.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Bootstrap" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="cmake">
<cmake_exe Condition="'$(cmake_exe)'==''">$([System.IO.Path]::GetFullPath('$(VCTargetsPath)..\..\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe'))</cmake_exe>
<VS15_x86>Visual Studio 15 2017</VS15_x86>
<VS15_x64>Visual Studio 15 2017 Win64</VS15_x64>
<!--VS16>Visual Studio 16 2019</VS16-->
<cmake_generator_name Condition="'$(cmake_generator_name)'=='' And !$([System.Environment]::Is64BitOperatingSystem)">$(VS15_x86)</cmake_generator_name>
<cmake_generator_name Condition="'$(cmake_generator_name)'=='' And $([System.Environment]::Is64BitOperatingSystem)">$(VS15_x64)</cmake_generator_name>
<cmake_output_directory Condition="'$(cmake_output_directory)'==''">$(MSBuildThisFileDirectory)$(cmake_generator_name.Replace(" ", "_"))</cmake_output_directory>
<CONFIG Condition="'$(CONFIG)'==''">Release</CONFIG>
<!--CONFIG>MinSizeRel</CONFIG-->
</PropertyGroup>
<Target Name="CMake" Condition="Exists('$(cmake_exe)')">
<MakeDir
Condition="!Exists('$(cmake_output_directory)')"
Directories="$(cmake_output_directory)" />
<Exec
Command="&quot;$(cmake_exe)&quot; -G &quot;$(cmake_generator_name)&quot; &quot;$(MSBuildThisFileDirectory)&quot;" WorkingDirectory="$(cmake_output_directory)" />
<Exec
Command="&quot;$(cmake_exe)&quot; --build &quot;$(cmake_output_directory)&quot; --config $(CONFIG)" />
</Target>
<Target Name="Bootstrap" DependsOnTargets="CMake" />
</Project>
cmake_minimum_required(VERSION 2.8.12)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "Configuration process cannot start from project source directory.")
endif()
project("Windows_Service")
add_executable(service "${CMAKE_SOURCE_DIR}/service.c")
target_compile_definitions(service PUBLIC UNICODE PUBLIC _UNICODE)
add_executable(service_manager "${CMAKE_SOURCE_DIR}/service_manager.c")
target_compile_definitions(service_manager PUBLIC UNICODE PUBLIC _UNICODE)
if(MSVC)
set(FLAGS "${FLAGS} /W4 /GS")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DEFAULT_CMAKE_C_FLAGS} ${FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DEFAULT_CMAKE_CXX_FLAGS} ${FLAGS}")
if(CMAKE_CL_64)
set(LINK_FLAGS "${LINK_FLAGS} /DynamicBase /NXCompat")
else()
set(LINK_FLAGS "${LINK_FLAGS} /SafeSEH /DynamicBase /NXCompat")
endif()
endif()
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 https://github.com/TheVice/
*
*/
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define SERVICE_NAME L"Some"
static SERVICE_STATUS gStatus;
static SERVICE_STATUS_HANDLE gStatusHandle;
static HANDLE gStopEvent = INVALID_HANDLE_VALUE;
#define CLOSE_HANDLE(aHandle) if (INVALID_HANDLE_VALUE != (aHandle)) { CloseHandle(aHandle); (aHandle) = INVALID_HANDLE_VALUE; }
static BOOL ReportServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
/**/
gStatus.dwCurrentState = dwCurrentState;
gStatus.dwWin32ExitCode = dwWin32ExitCode;
gStatus.dwWaitHint = dwWaitHint;
if (SERVICE_START_PENDING == dwCurrentState)
{
gStatus.dwControlsAccepted = 0;
}
else
{
gStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
}
if ((SERVICE_RUNNING == dwCurrentState) || (SERVICE_STOPPED == dwCurrentState))
{
gStatus.dwCheckPoint = 0;
dwCheckPoint = 1;
}
else
{
gStatus.dwCheckPoint = dwCheckPoint++;
}
return SetServiceStatus(gStatusHandle, &gStatus);
}
static VOID OnServiceStart(DWORD dwNumServicesArgs, LPWSTR* lpServiceArgVectors)
{
(void)dwNumServicesArgs;
(void)lpServiceArgVectors;
/**/
CLOSE_HANDLE(gStopEvent);
gStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
/*gStopEvent = CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS);
ResetEvent(gStopEvent);*/
if (NULL == gStopEvent)
{
ReportServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
ReportServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);
/**/
WaitForSingleObject(gStopEvent, INFINITE);
ReportServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
}
static VOID OnServiceStop()
{
ReportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(gStopEvent);
ReportServiceStatus(gStatus.dwCurrentState, NO_ERROR, 0);
}
#if 0
static VOID OnServiceShutdown()
{
/*TODO:*/
}
#endif
static VOID ServiceCtrlHandler(DWORD dwControl)
{
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
/*case SERVICE_CONTROL_PAUSE:*/
OnServiceStop();
break;
case SERVICE_CONTROL_CONTINUE:
OnServiceStart(0, NULL);
break;
case SERVICE_CONTROL_INTERROGATE:
break;
#if 0
case SERVICE_CONTROL_SHUTDOWN:
OnServiceShutdown();
break;
case SERVICE_CONTROL_PARAMCHANGE:
case SERVICE_CONTROL_NETBINDADD:
case SERVICE_CONTROL_NETBINDREMOVE:
case SERVICE_CONTROL_NETBINDENABLE:
case SERVICE_CONTROL_NETBINDDISABLE:
case SERVICE_CONTROL_DEVICEEVENT:
case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
case SERVICE_CONTROL_POWEREVENT:
case SERVICE_CONTROL_SESSIONCHANGE:
case SERVICE_CONTROL_PRESHUTDOWN:
case SERVICE_CONTROL_TIMECHANGE:
case SERVICE_CONTROL_TRIGGEREVENT:
case SERVICE_CONTROL_LOWRESOURCES:
case SERVICE_CONTROL_SYSTEMLOWRESOURCES:
#endif
default:
break;
}
}
static VOID ServiceProc(DWORD dwNumServicesArgs, LPWSTR* lpServiceArgVectors)
{
memset(&gStatusHandle, 0, sizeof(SERVICE_STATUS_HANDLE));
gStatusHandle = RegisterServiceCtrlHandlerW(SERVICE_NAME, (LPHANDLER_FUNCTION)ServiceCtrlHandler);
if (0 == gStatusHandle)
{
return;
}
memset(&gStatus, 0, sizeof(SERVICE_STATUS));
gStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
/*
* serviceStatus.dwCurrentState = SERVICE_START_PENDING;
*
* DWORD dwControlsAccepted = 0;
* dwControlsAccepted |= SERVICE_ACCEPT_STOP;
* dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
* dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
* gStatus.dwControlsAccepted = dwControlsAccepted;
*
* serviceStatus.dwWin32ExitCode = NO_ERROR;
* gStatus.dwCheckPoint = 0;
* gStatus.dwWaitHint = 0;
* gStatus.dwServiceSpecificExitCode = 0;
*/
if (0 != ReportServiceStatus(SERVICE_START_PENDING, NO_ERROR, 5000))
{
OnServiceStart(dwNumServicesArgs, lpServiceArgVectors);
}
CLOSE_HANDLE(gStopEvent);
}
int wmain(int argc, wchar_t** argv)
{
(void)argc;
(void)argv;
/**/
SERVICE_TABLE_ENTRYW serviceTable[2];
memset(&serviceTable, 0, 2 * sizeof(SERVICE_TABLE_ENTRYW));
serviceTable[0].lpServiceName = SERVICE_NAME;
serviceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTIONW)ServiceProc;
/**/
const BOOL ret = StartServiceCtrlDispatcherW(serviceTable);
#ifndef NDEBUG
if (0 == ret)
{
const DWORD lastError = GetLastError();
switch (lastError)
{
case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
{
fwprintf(stderr,
L"This error is returned if the program is being run as a console application rather than as a service.\nIf the program will be run as a console application for debugging purposes, structure it such that service - specific code is not called when this error is returned.\n");
}
break;
case ERROR_INVALID_DATA:
{
fwprintf(stderr, L"The specified dispatch table contains entries that are not in the proper format.\n");
}
break;
case ERROR_SERVICE_ALREADY_RUNNING:
{
fwprintf(stderr,
L"The process has already called StartServiceCtrlDispatcher.Each process can call StartServiceCtrlDispatcher only one time.\n");
}
break;
default:
fwprintf(stderr, L"StartServiceCtrlDispatcherW failed with error 0x%08lx\n", lastError);
}
}
#endif
return (0 != ret) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 https://github.com/TheVice/
*
*/
#include <time.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <windows.h>
#include <Lm.h>
#include <NTSecAPI.h>
/*
* #include <ntstatus.h>
* #include <scesvc.h>
* #include <ntlsa.h>
*/
#pragma comment(lib, "Netapi32.lib")
#define CLOSE_SERVICE_HANDLE(aHandle) if (NULL != (aHandle)) { CloseServiceHandle(aHandle); (aHandle) = NULL; }
#define NET_API_BUFFER_FREE(aRet, aBuffer) if (NULL != (aBuffer)) { aRet = NetApiBufferFree(aBuffer); (aBuffer) = NULL; }
#define LSA_FREE_MEMORY(aLsaMemory) if (NULL != (aLsaMemory)) { LsaFreeMemory(aLsaMemory); (aLsaMemory) = NULL; }
#define CLOSE_HANDLE(aHandle) if (INVALID_HANDLE_VALUE != (aHandle)) { CloseHandle(aHandle); (aHandle) = INVALID_HANDLE_VALUE; }
#define LSA_CLOSE(aLsaHandle) if (NULL != (aLsaHandle)) { LsaClose(aLsaHandle); (aLsaHandle) = NULL; }
#define FREE(aMemory) if (NULL != (aMemory)) { free(aMemory); (aMemory) = NULL; }
#define INIT_LSA_STRING(input, output) \
{ \
output = (PLSA_UNICODE_STRING)malloc(sizeof(LSA_UNICODE_STRING)); \
\
if (NULL != output) \
{ \
memset(output, 0, sizeof(LSA_UNICODE_STRING)); \
const size_t size = wcslen(input); \
\
if (0 < size && size < 0x7fff) \
{ \
output->Length = (USHORT)(size * sizeof(wchar_t)); \
output->MaximumLength = output->Length + sizeof(wchar_t); \
output->Buffer = (PWSTR)malloc(output->Length); \
\
if (NULL != output->Buffer) \
{ \
memcpy(output->Buffer, input, output->Length); \
} \
else \
{ \
free(output); \
output = NULL; \
} \
} \
} \
}
#define RELEASE_LSA_STRING(string) \
{ \
if (NULL != string) \
{ \
FREE(string->Buffer); \
free(string); \
string = NULL; \
} \
}
#define STATUS_SUCCESS 0
#define LSA_LOOKUP_ISOLATED_AS_LOCAL 0x80000000
#define LSA_LOOKUP_RETURN_LOCAL_NAMES 0
static char IsUserExists(const wchar_t* machineName, const wchar_t* userName)
{
char isUserExists = 0;
LPUSER_INFO_0 buffer = NULL;
DWORD entriesRead = 0;
DWORD totalEntries = 0;
/**/
DWORD ret = NetUserEnum(machineName, 0, FILTER_NORMAL_ACCOUNT, (LPBYTE*)&buffer,
MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries, NULL);
switch (ret)
{
case NERR_Success:
{
for (DWORD i = 0; i < totalEntries; ++i)
{
if (0 == lstrcmpW(userName, buffer[i].usri0_name))
{
isUserExists = 1;
break;
}
}
}
break;
default:
fwprintf(stderr, L"NetUserEnum failed with result %lu.\n", ret);
break;
}
NET_API_BUFFER_FREE(ret, buffer);
/**/
return (NERR_Success == ret && 1 == isUserExists) ? 1 : 0;
}
static char IsGroupExists(const wchar_t* machineName, const wchar_t* localGroup)
{
char isGroupExists = 0;
PLOCALGROUP_INFO_0 buffer = NULL;
DWORD entriesRead = 0;
DWORD totalEntries = 0;
DWORD ret = NetLocalGroupEnum(machineName, 0,
(LPBYTE*)&buffer, MAX_PREFERRED_LENGTH, &entriesRead,
&totalEntries, NULL);
switch (ret)
{
case NERR_Success:
{
for (DWORD i = 0; i < totalEntries; ++i)
{
if (0 == lstrcmpW(localGroup, buffer[i].lgrpi0_name))
{
isGroupExists = 1;
break;
}
}
}
break;
default:
fwprintf(stderr, L"NetLocalGroupEnum failed with result %lu.\n", ret);
break;
}
NET_API_BUFFER_FREE(ret, buffer);
/**/
return (NERR_Success == ret && 1 == isGroupExists) ? 1 : 0;
}
static char IsUserInGroup(const wchar_t* machineName, const wchar_t* localGroup, const wchar_t* userName)
{
char isUserInGroup = 0;
PLOCALGROUP_MEMBERS_INFO_1 buffer = NULL;
DWORD entriesRead = 0;
DWORD totalEntries = 0;
DWORD ret = NetLocalGroupGetMembers(machineName, localGroup, 1, (LPBYTE*)&buffer, MAX_PREFERRED_LENGTH,
&entriesRead, &totalEntries, NULL);
switch (ret)
{
case NERR_Success:
{
for (DWORD i = 0; i < totalEntries; ++i)
{
if (0 == lstrcmpW(userName, buffer[i].lgrmi1_name))
{
isUserInGroup = 1;
break;
}
}
}
break;
default:
fwprintf(stderr, L"NetLocalGroupGetMembers failed with result %lu.\n", ret);
break;
}
NET_API_BUFFER_FREE(ret, buffer);
/**/
return (NERR_Success == ret && 1 == isUserInGroup) ? 1 : 0;
}
static char AddUserToGroup(const wchar_t* machineName, const wchar_t* localGroup, const wchar_t* userName,
BOOL add)
{
if (NULL != localGroup && NULL != userName)
{
LOCALGROUP_MEMBERS_INFO_3 member;
memset(&member, 0, sizeof(LOCALGROUP_MEMBERS_INFO_3));
member.lgrmi3_domainandname = (LPWSTR)userName;
DWORD ret = NERR_Success;
if (FALSE != add)
{
ret = NetLocalGroupAddMembers(machineName, localGroup, 3, (LPBYTE)&member, 1);
}
else
{
ret = NetLocalGroupDelMembers(machineName, localGroup, 3, (LPBYTE)&member, 1);
}
return NERR_Success == ret;
}
return 0;
}
static char RemoveUserFromGroup(const wchar_t* machineName, const wchar_t* localGroup,
const wchar_t* userName)
{
return AddUserToGroup(machineName, localGroup, userName, FALSE);
}
static char EnterToUser(const wchar_t* machineName, const wchar_t* userName, const wchar_t* password)
{
char isLoginSuccess = 1;
HANDLE token = INVALID_HANDLE_VALUE;
BOOL ret = LogonUserW(userName, NULL == machineName ? L"." : machineName, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &token);
if (0 == ret)
{
/*fwprintf(stderr, L"LogonUserW failed with error 0x%08lx\n", GetLastError());*/
isLoginSuccess = 0;
}
CLOSE_HANDLE(token);
return (1 == isLoginSuccess) ? 1 : 0;
}
#define PASSWORD_SIZE 9
static void GeneratePassword(wchar_t** password)
{
static const wchar_t symbols[9] = { L'a', L'b', L'c', L'1', L'2', L'3', L'!', L'@', L'#' };
/**/
const int symbol_counts = (int)(sizeof(symbols) / sizeof(wchar_t));
assert(PASSWORD_SIZE <= symbol_counts);
if (symbol_counts < PASSWORD_SIZE)
{
(*password) = NULL;
return;
}
char symbols_nums[9];
memset(&symbols_nums, 0, 9);
/**/
(*password) = malloc((PASSWORD_SIZE + 1) * sizeof(wchar_t));
if (NULL == (*password))
{
return;
}
srand((unsigned int)(time(NULL)));
for (char i = 0; i < PASSWORD_SIZE; ++i)
{
int num = rand() % symbol_counts;
if (0 == symbols_nums[num])
{
(*password)[i] = symbols[num];
symbols_nums[num] = 1;
}
else
{
char next = 0;
int j = num;
while ((++j) < symbol_counts)
{
if (0 == symbols_nums[j])
{
(*password)[i] = symbols[j];
symbols_nums[j] = 1;
next = 1;
break;
}
}
if (1 == next)
{
continue;
}
j = num;
while (0 <= (--j))
{
if (0 == symbols_nums[j])
{
(*password)[i] = symbols[j];
symbols_nums[j] = 1;
break;
}
}
}
}
(*password)[PASSWORD_SIZE] = '\0';
fwprintf(stdout, L"Generated password is \"%s\" without \"(quot) symbols.\n", (*password));
}
static char AddUser(const wchar_t* machineName, const wchar_t* localGroup,
const wchar_t* userName, const wchar_t* password)
{
DWORD parm_err = 0;
USER_INFO_1 ui;
memset(&ui, 0, sizeof(USER_INFO_1));
ui.usri1_flags = UF_SCRIPT | UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD/*| UF_NORMAL_ACCOUNT*/;
ui.usri1_priv = USER_PRIV_USER;
ui.usri1_name = (LPWSTR)userName;
ui.usri1_password = (LPWSTR)password;
DWORD ret = NetUserAdd(machineName, 1, (LPBYTE)&ui, &parm_err);
switch (ret)
{
case NERR_Success:
{
fwprintf(stdout, L"Success created user '%s' on '%s'.\n", userName, machineName);
/*ret = */AddUserToGroup(machineName, localGroup, userName, TRUE);
ret = EnterToUser(machineName, userName, ui.usri1_password);
ret = (1 == ret) ? NERR_Success : ret;
break;
}
case ERROR_ACCESS_DENIED:
{
fwprintf(stderr, L"The user does not have access to the requested information.\n");
}
break;
case NERR_InvalidComputer:
{
fwprintf(stderr, L"The computer name is invalid.\n");
}
break;
case NERR_NotPrimary:
{
fwprintf(stderr, L"The operation is allowed only on the primary domain controller of the domain.\n");
}
break;
case NERR_GroupExists:
{
fwprintf(stderr, L"The group already exists.\n");
}
break;
case NERR_UserExists:
{
fwprintf(stderr, L"The user account already exists.\n");
}
break;
case NERR_PasswordTooShort:
{
fwprintf(stderr,
L"The password is shorter than required.\n(The password could also be too long, be too recent in its change history, not have enough unique characters, or not meet another password policy requirement.)\n");
}
break;
default:
break;
}
return (NERR_Success == ret) ? 1 : 0;
}
static char RemoveUser(const wchar_t* machineName, const wchar_t* userName)
{
const DWORD ret = NetUserDel(machineName, userName);
switch (ret)
{
case NERR_Success:
{
fwprintf(stdout, L"User '%s' was removed from '%s'.\n", userName, machineName);
}
break;
case ERROR_ACCESS_DENIED:
{
fwprintf(stderr, L"The user does not have access to the requested information.\n");
}
break;
case NERR_InvalidComputer:
{
fwprintf(stderr, L"The computer name is invalid.\n");
}
break;
case NERR_NotPrimary:
{
fwprintf(stderr, L"The operation is allowed only on the primary domain controller of the domain.\n");
}
break;
case NERR_UserNotFound:
{
fwprintf(stderr, L"The user name could not be found.\n");
}
break;
default:
break;
}
return (NERR_Success == ret) ? 1 : 0;
}
static char IsUserCanRunService(const wchar_t* userName)
{
if (NULL == userName)
{
return 0;
}
LSA_OBJECT_ATTRIBUTES objectAttributes;
memset(&objectAttributes, 0, sizeof(LSA_OBJECT_ATTRIBUTES));
objectAttributes.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
/**/
const ACCESS_MASK desiredAccess = POLICY_READ | POLICY_EXECUTE;
LSA_HANDLE policyHandle = NULL;
/**/
PLSA_UNICODE_STRING systemName = NULL;
INIT_LSA_STRING(L"", systemName);
NTSTATUS result = LsaOpenPolicy(systemName, &objectAttributes, desiredAccess, &policyHandle);
RELEASE_LSA_STRING(systemName);
if (STATUS_SUCCESS != result)
{
fwprintf(stderr,
L"LsaOpenPolicy failed. NTSTATUS - '%i' LsaNtStatusToWinError - '%lu' GetLastError - '0x%08lx'.\n",
result, LsaNtStatusToWinError(result), GetLastError());
return 0;
}
PLSA_UNICODE_STRING userRight = NULL;
PVOID buffer = NULL;
ULONG countReturned = 0;
INIT_LSA_STRING(SE_SERVICE_LOGON_NAME, userRight);
result = LsaEnumerateAccountsWithUserRight(policyHandle, userRight, &buffer, &countReturned);
RELEASE_LSA_STRING(userRight);
if (STATUS_SUCCESS != result)
{
fwprintf(stderr,
L"LsaEnumerateAccountsWithUserRight failed. NTSTATUS - '%i' LsaNtStatusToWinError - '%lu' GetLastError - '0x%08lx'.\n",
result,
LsaNtStatusToWinError(result), GetLastError());
LSA_FREE_MEMORY(buffer);
LSA_CLOSE(policyHandle);
return 0;
}
PSID* sids = (PSID*)malloc(countReturned * sizeof(PSID));
if (NULL == sids)
{
LSA_FREE_MEMORY(buffer);
LSA_CLOSE(policyHandle);
return 0;
}
for (ULONG i = 0; i < countReturned; ++i)
{
sids[i] = ((LSA_ENUMERATION_INFORMATION*)(buffer))[i].Sid;
}
PLSA_REFERENCED_DOMAIN_LIST referencedDomains = NULL;
PLSA_TRANSLATED_NAME names = NULL;
result = LsaLookupSids2(policyHandle, LSA_LOOKUP_RETURN_LOCAL_NAMES, countReturned,
sids, &referencedDomains, &names);
FREE(sids);
LSA_FREE_MEMORY(referencedDomains);
LSA_FREE_MEMORY(buffer);
if (STATUS_SUCCESS != result)
{
fwprintf(stderr,
L"LsaLookupSids2 failed. NTSTATUS - '%i' LsaNtStatusToWinError - '%lu' GetLastError - '0x%08lx'.\n",
result,
LsaNtStatusToWinError(result), GetLastError());
LSA_FREE_MEMORY(names);
LSA_CLOSE(policyHandle);
return 0;
}
const size_t userNameSize = wcslen(userName) * sizeof(wchar_t);
for (ULONG i = 0; i < countReturned; ++i)
{
if (SidTypeUser == names[i].Use &&
userNameSize == names[i].Name.Length &&
0 == memcmp(userName, names[i].Name.Buffer, userNameSize))
{
result = 1;
break;
}
}
LSA_FREE_MEMORY(names);
LSA_CLOSE(policyHandle);
/**/
return (result == 1) ? 1 : 0;
}
static char AllowUserRunService(const wchar_t* userName, BOOL allow)
{
if (NULL == userName)
{
return 0;
}
LSA_OBJECT_ATTRIBUTES objectAttributes;
memset(&objectAttributes, 0, sizeof(LSA_OBJECT_ATTRIBUTES));
objectAttributes.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
/**/
const ACCESS_MASK desiredAccess = POLICY_ALL_ACCESS;
LSA_HANDLE policyHandle = NULL;
/**/
PLSA_UNICODE_STRING systemName = NULL;
INIT_LSA_STRING(L"", systemName);
NTSTATUS result = LsaOpenPolicy(systemName, &objectAttributes, desiredAccess, &policyHandle);
RELEASE_LSA_STRING(systemName);
if (STATUS_SUCCESS != result)
{
fwprintf(stderr,
L"LsaOpenPolicy failed. NTSTATUS - '%i' LsaNtStatusToWinError - '%lu' GetLastError - '0x%08lx'.\n",
result, LsaNtStatusToWinError(result), GetLastError());
return 0;
}
PLSA_REFERENCED_DOMAIN_LIST domains = NULL;
PLSA_TRANSLATED_SID2 sids = NULL;
PLSA_UNICODE_STRING userName_ = NULL;
INIT_LSA_STRING(userName, userName_);
if (NULL == userName_)
{
return 0;
}
result = LsaLookupNames2(policyHandle, LSA_LOOKUP_ISOLATED_AS_LOCAL, 1, userName_, &domains, &sids);
RELEASE_LSA_STRING(userName_);
LSA_FREE_MEMORY(domains);
if (STATUS_SUCCESS != result)
{
fwprintf(stderr,
L"LsaLookupNames2 failed. NTSTATUS - '%i' LsaNtStatusToWinError - '%lu' GetLastError - '0x%08lx'.\n",
result, LsaNtStatusToWinError(result), GetLastError());
LSA_FREE_MEMORY(sids);
LSA_CLOSE(policyHandle);
return 0;
}
PLSA_UNICODE_STRING userRight = NULL;
PSID accountSid = sids->Sid;
INIT_LSA_STRING(SE_SERVICE_LOGON_NAME, userRight);
if (NULL == userRight)
{
LSA_FREE_MEMORY(sids);
accountSid = NULL;
LSA_CLOSE(policyHandle);
return 0;
}
if (FALSE != allow)
{
result = LsaAddAccountRights(policyHandle, accountSid, userRight, 1);
}
else
{
result = LsaRemoveAccountRights(policyHandle, accountSid, FALSE, userRight, 1);
}
RELEASE_LSA_STRING(userRight);
LSA_FREE_MEMORY(sids);
accountSid = NULL;
if (STATUS_SUCCESS != result)
{
fwprintf(stderr,
(0 != allow) ?
L"LsaAddAccountRights failed. NTSTATUS - '%i' LsaNtStatusToWinError - '%lu' GetLastError - '0x%08lx'.\n" :
L"LsaRemoveAccountRights failed. NTSTATUS - '%i' LsaNtStatusToWinError - '%lu' GetLastError - '0x%08lx'.\n",
result, LsaNtStatusToWinError(result), GetLastError());
}
LSA_CLOSE(policyHandle);
/**/
return (STATUS_SUCCESS != result) ? 0 : 1;
}
static char ProhibitUserRunService(const wchar_t* userName)
{
return AllowUserRunService(userName, FALSE);
}
static char IsServiceDelayedAutostart(LPSC_HANDLE service)
{
SERVICE_DELAYED_AUTO_START_INFO info;
memset(&info, 0, sizeof(SERVICE_DELAYED_AUTO_START_INFO));
DWORD bytesNeeded = 0;
const BOOL ret = QueryServiceConfig2W(*service, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (LPBYTE)&info,
sizeof(SERVICE_DELAYED_AUTO_START_INFO), &bytesNeeded);
/**/
return (0 != ret && TRUE == info.fDelayedAutostart) ? 1 : 0;
}
static char IsServiceWithSpecifiedNameExist(const wchar_t* machineName, const wchar_t* serviceName)
{
char isServiceDoesNotExist = 0;
SC_HANDLE scManager = OpenSCManagerW(machineName, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT/*| SC_MANAGER_ENUMERATE_SERVICE*/);
if (NULL == scManager)
{
fwprintf(stderr, L"OpenSCManagerW failed with error 0x%08lx\n", GetLastError());
return 0;
}
SC_HANDLE service = OpenServiceW(scManager, serviceName, STANDARD_RIGHTS_READ);
if (NULL == service)
{
const DWORD lastError = GetLastError();
isServiceDoesNotExist = (ERROR_SERVICE_DOES_NOT_EXIST == lastError);
if (1 != isServiceDoesNotExist)
{
fwprintf(stderr, L"OpenServiceW failed with error 0x%08lx\n", lastError);
}
}
#if 0
else
{
const char isServiceDelayedAutostart = IsServiceDelayedAutostart(&service);
}
#endif
CLOSE_SERVICE_HANDLE(service);
CLOSE_SERVICE_HANDLE(scManager);
/**/
return 1 != isServiceDoesNotExist;
}
static char MadeServiceDelayedAutostart(LPSC_HANDLE service, BOOL made)
{
SERVICE_DELAYED_AUTO_START_INFO info;
memset(&info, 0, sizeof(SERVICE_DELAYED_AUTO_START_INFO));
info.fDelayedAutostart = made;
const BOOL ret = ChangeServiceConfig2W(*service, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (LPVOID)&info);
/**/
return (0 != ret) ? 1 : 0;
}
static char InstallService(const wchar_t* machineName,
const wchar_t* serviceName,
const wchar_t* displayName,
const wchar_t* binaryPathName,
const wchar_t* userName,
const wchar_t* userPassword)
{
SC_HANDLE scManager = OpenSCManagerW(machineName, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (NULL == scManager)
{
/*ERROR_ACCESS_DENIED
*ERROR_DATABASE_DOES_NOT_EXIST*/
fwprintf(stderr, L"OpenSCManagerW failed with error 0x%08lx\n", GetLastError());
return 0;
}
SC_HANDLE service = CreateServiceW(
scManager,
serviceName,
displayName,
SERVICE_QUERY_STATUS,
SERVICE_WIN32_OWN_PROCESS/*SERVICE_USER_OWN_PROCESS*/,
/*SERVICE_AUTO_START*/SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE/*SERVICE_ERROR_NORMAL*/,
binaryPathName,
NULL/*MS Transactions*/,
NULL,
NULL,
userName,
userPassword
);
/**/
const char isServiceWasCreated = (NULL != service);
if (1 != isServiceWasCreated)
{
const DWORD lastError = GetLastError();
switch (lastError)
{
case ERROR_ACCESS_DENIED:
{
fwprintf(stderr,
L"The handle to the SCM database does not have the SC_MANAGER_CREATE_SERVICE access right.\n");
}
break;
case ERROR_CIRCULAR_DEPENDENCY:
{
fwprintf(stderr, L"A circular service dependency was specified.\n");
}
break;
case ERROR_DUPLICATE_SERVICE_NAME:
{
fwprintf(stderr,
L"The display name already exists in the service control manager database either as a service name or as another display name.\n");
}
break;
case ERROR_INVALID_HANDLE:
{
fwprintf(stderr, L"The handle to the specified service control manager database is invalid.\n");
}
break;
case ERROR_INVALID_NAME:
{
fwprintf(stderr, L"The specified service name is invalid.\n");
}
break;
case ERROR_INVALID_PARAMETER:
{
fwprintf(stderr, L"A parameter that was specified is invalid.\n");
}
break;
case ERROR_INVALID_SERVICE_ACCOUNT:
{
fwprintf(stderr, L"The user account name specified in the lpServiceStartName parameter does not exist.\n");
}
break;
case ERROR_SERVICE_EXISTS:
{
fwprintf(stderr, L"The specified service already exists in this database.\n");
}
break;
case ERROR_SERVICE_MARKED_FOR_DELETE:
{
fwprintf(stderr,
L"The specified service already exists in this database and has been marked for deletion.\n");
}
break;
default:
fwprintf(stderr, L"CreateServiceW failed with error 0x%08lx\n", lastError);
break;
}
}
#if 0
else
{
const char isServiceDelayedAutostart = MadeServiceDelayedAutostart(&service, TRUE);
}
#endif
CLOSE_SERVICE_HANDLE(service);
CLOSE_SERVICE_HANDLE(scManager);
/**/
return 1 == isServiceWasCreated;
}
static char UninstallService(const wchar_t* machineName,
const wchar_t* serviceName)
{
SC_HANDLE scManager = OpenSCManagerW(machineName, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
if (NULL == scManager)
{
fwprintf(stderr, L"OpenSCManagerW failed with error 0x%08lx\n", GetLastError());
return 0;
}
SC_HANDLE service = OpenServiceW(scManager, serviceName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE);
if (NULL == service)
{
fwprintf(stderr, L"OpenServiceW failed with error 0x%08lx\n", GetLastError());
CLOSE_SERVICE_HANDLE(service);
/**/
return 0;
}
DWORD bytesNeeded = 0;
if (0 == QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, NULL, 0, &bytesNeeded) &&
ERROR_INSUFFICIENT_BUFFER == GetLastError() &&
0 < bytesNeeded && sizeof(SERVICE_STATUS_PROCESS) == bytesNeeded);
{
char setToStop = 2;
const DWORD bufferSize = bytesNeeded;
BYTE* buffer = (BYTE*)malloc(bufferSize);
if (NULL != buffer)
{
while (0 != QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, buffer, bufferSize, &bytesNeeded) &&
SERVICE_STOPPED != ((SERVICE_STATUS_PROCESS*)buffer)->dwCurrentState)
{
if (1 != setToStop)
{
SERVICE_CONTROL_STATUS_REASON_PARAMS controlParams;
memset(&controlParams, 0, sizeof(SERVICE_CONTROL_STATUS_REASON_PARAMS));
controlParams.dwReason = SERVICE_STOP_REASON_FLAG_PLANNED | SERVICE_STOP_REASON_MAJOR_APPLICATION |
SERVICE_STOP_REASON_MINOR_SOFTWARE_UPDATE_UNINSTALL;
controlParams.pszComment = L"Service will be uninstalled.\0";
if (0 == ControlServiceExW(service, SERVICE_CONTROL_STOP, SERVICE_CONTROL_STATUS_REASON_INFO,
(VOID*)&controlParams))
{
fwprintf(stderr, L"ControlServiceExW failed with error 0x%08lx\n", GetLastError());
setToStop = 0;
break;
}
else
{
fwprintf(stdout, L"Stopping '%s' service.", serviceName);
}
setToStop = 1;
}
fwprintf(stdout, L".");
Sleep(2500);
}
FREE(buffer);
if (0 == setToStop)
{
CLOSE_SERVICE_HANDLE(service);
CLOSE_SERVICE_HANDLE(scManager);
/**/
return 0;
}
fwprintf(stdout, L"\n");
if (0 == DeleteService(service))
{
fwprintf(stderr, L"DeleteService failed with error 0x%08lx\n", GetLastError());
/**/
CLOSE_SERVICE_HANDLE(service);
CLOSE_SERVICE_HANDLE(scManager);
/**/
return 0;
}
}
}
/**/
CLOSE_SERVICE_HANDLE(service);
CLOSE_SERVICE_HANDLE(scManager);
/**/
return 1;
}
static char GetUserNameForService(const wchar_t* regularUserName, wchar_t** userNameForRunService)
{
if (NULL == regularUserName || NULL == userNameForRunService)
{
return 0;
}
(*userNameForRunService) = NULL;
size_t size = wcslen(regularUserName) * sizeof(wchar_t);
if (0 == size)
{
return 0;
}
static const wchar_t* subString = L".\\";
const wchar_t* position = wcsstr(regularUserName, subString);
if (position != regularUserName)
{
const size_t source_size = size;
size += 3 * sizeof(wchar_t);
(*userNameForRunService) = (wchar_t*)malloc(size);
if (NULL != (*userNameForRunService))
{
memset((*userNameForRunService), 0, size);
memcpy((*userNameForRunService), subString, 2 * sizeof(wchar_t));
memcpy(&(*userNameForRunService)[2], regularUserName, source_size);
/**/
return 1;
}
}
return 0;
}
#define COMMAND argv[1]
#define MACHINE_NAME NULL
#define SERVICE_NAME argv[2]
#define DISPLAY_NAME argv[3]
#define BINARY_PATH argv[4]
#define USER_NAME argv[5]
#define USER_PASSWORD argv[6]
#define LOCAL_GROUP L"Users"
int wmain(int argc, wchar_t** argv)
{
const char install = (1 < argc && 0 == lstrcmpW(L"install", COMMAND));
const char uninstall = (1 < argc && 0 == lstrcmpW(L"uninstall", COMMAND));
for (int i = 1; i < argc; ++i)
{
switch (i)
{
case 1:
fwprintf(stdout, L"Command: ");
break;
case 2:
fwprintf(stdout, L"Service name: ");
break;
case 3:
fwprintf(stdout, L"Display name: ");
break;
case 4:
fwprintf(stdout, L"Binary path: ");
break;
case 5:
fwprintf(stdout, L"User name: ");
break;
case 6:
fwprintf(stdout, L"User password: ");
break;
default:
fwprintf(stdout, L"Unknown argument: ");
break;
}
fwprintf(stdout, L"%s\n", argv[i]);
}
if (7 != argc || (0 == install && 0 == uninstall))
{
fwprintf(stderr,
L"Sample using:\n %s COMMAND [install|uninstall] SERVICE_NAME DISPLAY_NAME BINARY_PATH USER_NAME USER_PASSWORD\nPlease note that password will be used only if account early created, otherwise last one will be generated\n",
argv[0]);
fwprintf(stderr,
L"To uninstall service and remove user at once - password must be valid. Otherwise only service will be uninstalled and user should be removed manually.\n");
fwprintf(stderr,
L"While uninstall start - if user should not save possibility to run services - name of last should be valid.\n");
/**/
return EXIT_FAILURE;
}
char isServiceWithSpecifiedNameExist = IsServiceWithSpecifiedNameExist(MACHINE_NAME, SERVICE_NAME);
if (0 != isServiceWithSpecifiedNameExist)
{
if (0 != uninstall)
{
const char isServiceUninstalled = UninstallService(MACHINE_NAME, SERVICE_NAME);
if (0 == isServiceUninstalled)
{
return EXIT_FAILURE;
}
const char isUserExists_ = IsUserExists(MACHINE_NAME, USER_NAME);
if (isUserExists_ && 0 != IsUserCanRunService(USER_NAME))
{
const char isRunServiceProhibit4User = ProhibitUserRunService(USER_NAME);
if (0 == isRunServiceProhibit4User)
{
return EXIT_FAILURE;
}
if (0 != EnterToUser(MACHINE_NAME, USER_NAME, USER_PASSWORD))
{
/**/RemoveUserFromGroup(MACHINE_NAME, LOCAL_GROUP, USER_NAME);
return (0 != RemoveUser(MACHINE_NAME, USER_NAME)) ? EXIT_SUCCESS : EXIT_FAILURE;
}
}
}
return EXIT_SUCCESS;
}
char isUserExists = IsUserExists(MACHINE_NAME, USER_NAME);
wchar_t* password = NULL;
if (0 == isUserExists)
{
const char isGroupExists = IsGroupExists(MACHINE_NAME, LOCAL_GROUP);
GeneratePassword(&password);
isUserExists = AddUser(MACHINE_NAME, (0 != isGroupExists) ? LOCAL_GROUP : NULL,
USER_NAME, password);
if (0 == isUserExists)
{
SecureZeroMemory((PVOID)password, PASSWORD_SIZE);
FREE(password);
/**/
return EXIT_FAILURE;
}
}
char isUserCanRunService = IsUserCanRunService(USER_NAME);
if (0 == isUserCanRunService)
{
isUserCanRunService = AllowUserRunService(USER_NAME, TRUE);
if (0 == isUserCanRunService)
{
if (NULL != password)
{
SecureZeroMemory((PVOID)password, PASSWORD_SIZE);
FREE(password);
}
return EXIT_FAILURE;
}
}
wchar_t* userName = NULL;
GetUserNameForService(USER_NAME, &userName);
if (NULL != password)
{
isServiceWithSpecifiedNameExist = InstallService(
MACHINE_NAME, SERVICE_NAME,
DISPLAY_NAME, BINARY_PATH,
userName, password);
/**/
SecureZeroMemory((PVOID)password, PASSWORD_SIZE);
FREE(password);
}
else
{
isServiceWithSpecifiedNameExist = InstallService(
MACHINE_NAME, SERVICE_NAME,
DISPLAY_NAME, BINARY_PATH,
userName, USER_PASSWORD);
}
FREE(userName);
/**/
return (0 != isServiceWithSpecifiedNameExist) ? EXIT_SUCCESS : EXIT_FAILURE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment