Примечание. Это не относится к службам перечисления как другому пользователю, но, учитывая более широкое описание того, что вы делаете, я думаю, что это хороший ответ.
Я думаю, что вы можете значительно упростить это и, возможно, избежать части проблемы с безопасностью, если перейдете непосредственно к интересующей службе. Вместо вызова GetServices попробуйте это:
string machineName = ConfigurationManager.AppSettings["ServiceMachineName"];
string serviceName = ConfigurationManager.AppSettings["ServiceName"];
ServiceController service = new ServiceController( serviceName, machineName );
return service.Status;
Это напрямую связано с интересующей службой и обходит этап перечисления / поиска. Следовательно, не требуется, чтобы у вызывающего абонента было право SC_MANAGER_ENUMERATE_SERVICE
на диспетчер управления службами (SCM), которого удаленные пользователи по умолчанию не имеют. Это все еще требует SC_MANAGER_CONNECT
, но в соответствии с MSDN , которое должно быть предоставлено удаленным аутентифицированным пользователям.
После того, как вы нашли интересующую службу, вам все равно нужно будет остановить и запустить ее, что, вероятно, не имеют права ваши удаленные пользователи. Тем не менее, можно изменить дескриптор безопасности (DACL) для отдельных служб, что позволит вам предоставить удаленным пользователям доступ для остановки и запуска службы, не требуя, чтобы они были локальными администраторами. Это делается с помощью API-функции SetNamedSecurityInfo . Права доступа, которые вам нужно предоставить, - SERVICE_START
и SERVICE_STOP
. В зависимости от того, к каким именно группам принадлежат эти пользователи, вам также может потребоваться предоставить им GENERIC_READ
. Все эти права описаны в MSDN .
Вот некоторый код C ++, который будет выполнять эту настройку, предполагая, что интересующие вас пользователи находятся в группе «Remote Service Controllers» (которую вы бы создали), а имя службы - «my-service-name». Обратите внимание, что если вы хотите предоставить доступ к известной группе, такой как «Пользователи» (не обязательно хорошая идея), а не к группе, которую вы создали, вам нужно изменить TRUSTEE_IS_GROUP
на TRUSTEE_IS_WELL_KNOWN_GROUP
.
В коде нет проверки ошибок, которую вы хотели бы добавить. Все три функции, которые могут потерпеть неудачу (Get / SetNamedSecurityInfo и SetEntriesInAcl), возвращают 0, чтобы указать успех.
Другое примечание: Вы также можете установить дескриптор безопасности службы, используя инструмент SC , который можно найти в папке% WINDIR% \ System32, но который не требует программирования.
#include "windows.h"
#include "accctrl.h"
#include "aclapi.h"
int main()
{
char serviceName[] = "my-service-name";
char userGroup[] = "Remote Service Controllers";
// retrieve the security info
PACL pDacl = NULL;
PSECURITY_DESCRIPTOR pDescriptor = NULL;
GetNamedSecurityInfo( serviceName, SE_SERVICE,
DACL_SECURITY_INFORMATION, NULL, NULL,
&pDacl, NULL, &pDescriptor );
// add an entry to allow the users to start and stop the service
EXPLICIT_ACCESS access;
ZeroMemory( &access, sizeof(access) );
access.grfAccessMode = GRANT_ACCESS;
access.grfAccessPermissions = SERVICE_START | SERVICE_STOP;
access.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
access.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
access.Trustee.ptstrName = userGroup;
PACL pNewDacl;
SetEntriesInAcl( 1, &access, pDacl, &pNewDacl );
// write the changes back to the service
SetNamedSecurityInfo( serviceName, SE_SERVICE,
DACL_SECURITY_INFORMATION, NULL, NULL,
pNewDacl, NULL );
LocalFree( pNewDacl );
LocalFree( pDescriptor );
}
Это также можно сделать из C # с использованием P / Invoke, но это немного больше работы.
Если вы все еще хотите иметь возможность перечислять службы в качестве этих пользователей, вам нужно предоставить им SC_MANAGER_ENUMERATE_SERVICE
прямо в SCM. К сожалению, в соответствии с MSDN , безопасность SCM может быть изменена только в Windows Server 2003 sp1 или более поздней версии.