Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
141ecd9
Add -SecurityDescriptor parameter to Set-Service
kvprasoon Jan 5, 2019
69d909a
Define psecurityDescriptor
kvprasoon Jan 5, 2019
f00e686
Merge branch 'master' into srv-sdset
kvprasoon Jan 11, 2019
6eff7fe
change in status variable
kvprasoon Jan 11, 2019
9455274
Merge branch 'master' into srv-sdset
kvprasoon Jan 21, 2019
01569ca
DACL Support only
kvprasoon Jan 21, 2019
7f317e3
Merge branch 'master' into srv-sdset
kvprasoon Jan 26, 2019
8c28270
Addressing review comments
kvprasoon Jan 26, 2019
e1d77a4
Merge branch 'master' into srv-sdset
kvprasoon Feb 11, 2019
c56687e
add unit tests
kvprasoon Feb 11, 2019
5fdfc7a
using pinned handle
kvprasoon Mar 9, 2019
312772a
Merge branch 'master' into srv-sdset
kvprasoon Mar 9, 2019
042ee84
Fix sddl handle in Set-Service
IISResetMe Mar 10, 2019
bbe5907
Merge pull request #2 from IISResetMe/srv-sdset
kvprasoon Mar 11, 2019
7c1a761
add unit tests
kvprasoon Mar 12, 2019
7368a4d
review comment change for error msg and const code
kvprasoon Mar 12, 2019
322a2fe
Merge branch 'master' into srv-sdset
kvprasoon Mar 25, 2019
e43faca
adding comment as per review
kvprasoon Mar 30, 2019
ded4dbd
Merge branch 'master' into srv-sdset
kvprasoon Mar 30, 2019
0b9a7bb
adding comment as per review
kvprasoon Mar 30, 2019
2067c21
adding comment as per review
kvprasoon Mar 30, 2019
9a96aac
revert dummy service executable creation
kvprasoon Apr 1, 2019
ab4fbd0
review comment fixes
kvprasoon Apr 2, 2019
644e2d0
random password generation
kvprasoon Apr 5, 2019
c77cf2c
changes as per suggestion
kvprasoon Apr 6, 2019
dc74cd5
Merge branch 'master' into srv-sdset
kvprasoon Apr 6, 2019
3540368
Merge branch 'master' into srv-sdset
kvprasoon Apr 17, 2019
b391b9f
Merge branch 'master' into srv-sdset
kvprasoon May 9, 2019
e1bfb25
Merge branch 'master' into srv-sdset
kvprasoon May 14, 2019
6f97b7a
Merge branch 'master' into srv-sdset
kvprasoon May 31, 2019
e81813e
removing explicit loading of HelpersCommon module
kvprasoon Jun 2, 2019
acb8a44
Merge branch 'master' into srv-sdset
kvprasoon Jun 25, 2019
a87a0d4
Merge branch 'master' into srv-sdset
kvprasoon Jul 2, 2019
761778e
create complex password
kvprasoon Jul 3, 2019
765a104
Merge branch 'master' into srv-sdset
kvprasoon Jul 4, 2019
51add23
create complex pwd
kvprasoon Jul 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using System.Runtime.Serialization;
using System.Runtime.InteropServices; // Marshal, DllImport
using System.Security.Permissions;
using System.Security.AccessControl;
using NakedWin32Handle = System.IntPtr;
using DWORD = System.UInt32;

Expand Down Expand Up @@ -1560,10 +1561,23 @@ public ServiceStartupType StartupType
startupType = value;
}
}

// We set the initial value to an invalid value so that we can
// distinguish when this is and is not set.
internal ServiceStartupType startupType = ServiceStartupType.InvalidValue;

/// <summary>
/// Sets the SecurityDescriptorSddl of the service using a SDDL string.
/// </summary>
[Parameter]
[Alias("sd")]
[ValidateNotNullOrEmpty]
public string SecurityDescriptorSddl
{
get;
set;
}

/// <summary>
/// The following is the definition of the input parameter "Status".
/// This specifies what state the service should be in (e.g. Running, Stopped,
Expand Down Expand Up @@ -1718,8 +1732,9 @@ protected override void ProcessRecord()
hService = NativeMethods.OpenServiceW(
hScManager,
Name,
NativeMethods.SERVICE_CHANGE_CONFIG
NativeMethods.SERVICE_CHANGE_CONFIG | NativeMethods.WRITE_DAC | NativeMethods.WRITE_OWNER
);

if (IntPtr.Zero == hService)
{
int lastError = Marshal.GetLastWin32Error();
Expand Down Expand Up @@ -1870,6 +1885,37 @@ protected override void ProcessRecord()
}
}

if(!string.IsNullOrEmpty(SecurityDescriptorSddl))
{
var rawSecurityDescriptor = new RawSecurityDescriptor(SecurityDescriptorSddl);
RawAcl rawDiscretionaryAcl = rawSecurityDescriptor.DiscretionaryAcl ;
var discretionaryAcl = new DiscretionaryAcl (false, false, rawDiscretionaryAcl );

byte[] rawDacl = new byte[discretionaryAcl.BinaryLength];
discretionaryAcl.GetBinaryForm(rawDacl, 0);
rawSecurityDescriptor.DiscretionaryAcl = new RawAcl(rawDacl, 0);
byte[] securityDescriptorByte = new byte[rawSecurityDescriptor.BinaryLength];
rawSecurityDescriptor.GetBinaryForm(securityDescriptorByte, 0);

status = NativeMethods.SetServiceObjectSecurity(
hService,
SecurityInfos.DiscretionaryAcl,
securityDescriptorByte);

if (!status)
{
int lastError = Marshal.GetLastWin32Error();
Win32Exception exception = new Win32Exception(lastError);
bool accessDenied = exception.NativeErrorCode == NativeMethods.ERROR_ACCESS_DENIED;
WriteNonTerminatingError(
service,
exception,
nameof(ServiceResources.CouldNotSetServiceSecurityDescriptorSddl),
StringUtil.Format(ServiceResources.CouldNotSetServiceSecurityDescriptorSddl, Name, exception.Message),
accessDenied ? ErrorCategory.PermissionDenied : ErrorCategory.InvalidOperation);
}
}

if (PassThru.IsPresent)
{
// To display the service, refreshing the service would not show the display name after updating
Expand Down Expand Up @@ -2554,6 +2600,7 @@ internal static class NativeMethods
internal const int ERROR_SERVICE_ALREADY_RUNNING = 1056;
internal const int ERROR_SERVICE_NOT_ACTIVE = 1062;
internal const int ERROR_INSUFFICIENT_BUFFER = 122;
internal const DWORD ERROR_ACCESS_DENIED = 0x5;
internal const DWORD SC_MANAGER_CONNECT = 1;
internal const DWORD SC_MANAGER_CREATE_SERVICE = 2;
internal const DWORD SC_MANAGER_ALL_ACCESS = 0xf003f;
Expand All @@ -2567,7 +2614,8 @@ internal static class NativeMethods
internal const DWORD SERVICE_CONFIG_DESCRIPTION = 1;
internal const DWORD SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
internal const DWORD SERVICE_CONFIG_SERVICE_SID_INFO = 5;

internal const DWORD WRITE_DAC = 262144;
internal const DWORD WRITE_OWNER =524288;
internal const DWORD SERVICE_WIN32_OWN_PROCESS = 0x10;
internal const DWORD SERVICE_ERROR_NORMAL = 1;

Expand Down Expand Up @@ -2688,6 +2736,16 @@ NakedWin32Handle CreateServiceW(
[In] IntPtr lpPassword
);


[DllImport(PinvokeDllNames.SetServiceObjectSecurityDllName, CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern
bool SetServiceObjectSecurity(
NakedWin32Handle hSCManager,
System.Security.AccessControl.SecurityInfos dwSecurityInformation,
byte[] lpSecurityDescriptor
);

/// <summary>
/// CreateJobObject API creates or opens a job object.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@
<data name="CouldNotSetServiceDelayedAutoStart" xml:space="preserve">
<value>Service '{1} ({0})' automatic (delayed start) cannot be configured due to the following error: {2}</value>
</data>
<data name="CouldNotSetServiceSecurityDescriptorSddl" xml:space="preserve">
<value>Service '{0}' security descriptor cannot be configured due to the following error: {1}</value>
</data>
<data name="CouldNotNewService" xml:space="preserve">
<value>Service '{1} ({0})' cannot be created due to the following error: {2}</value>
</data>
Expand All @@ -179,7 +182,7 @@
</data>
<data name="CouldNotRemoveService" xml:space="preserve">
<value>Service '{1} ({0})' cannot be removed due to the following error: {2}</value>
</data>
</data>
<data name="CouldNotAccessDependentServices" xml:space="preserve">
<value>'Cannot access dependent services of '{1} ({0})'</value>
</data>
Expand Down
1 change: 1 addition & 0 deletions src/System.Management.Automation/utils/PInvokeDllNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,6 @@ internal static class PinvokeDllNames
internal const string DeleteServiceDllName = "api-ms-win-service-management-l1-1-0.dll"; /*124*/
internal const string QueryServiceConfigDllName = "api-ms-win-service-management-l2-1-0.dll"; /*125*/
internal const string QueryServiceConfig2DllName = "api-ms-win-service-management-l2-1-0.dll"; /*126*/
internal const string SetServiceObjectSecurityDllName = "api-ms-win-service-management-l2-1-0.dll"; /*127*/
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Import-Module (Join-Path -Path $PSScriptRoot '..\Microsoft.PowerShell.Security\certificateCommon.psm1')

Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows" {
BeforeAll {
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
Expand All @@ -8,14 +10,16 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
}
if ($IsWindows) {
$userName = "testuserservices"
$testPass = "Secret123!"
net user $userName $testPass /add > $null
$password = ConvertTo-SecureString $testPass -AsPlainText -Force
$creds = [pscredential]::new(".\$userName", $password)
$Password = ([char[]]([char]33..[char]95) + ([char[]]([char]97..[char]126)) + 0..9 | Sort-Object {Get-Random})[0..12] -join ''
$testPass = (New-Object -TypeName Net.NetworkCredential("", $Password)).SecurePassword
$creds = [pscredential]::new(".\$userName", $testPass)
$SecurityDescriptorSddl = 'D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;SU)'
$WrongSecurityDescriptorSddl = 'D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BB)(A;;CCLCSWLOCRRC;;;SU)'
net user $userName $creds.GetNetworkCredential().Password /add > $null

$testservicename1 = "testservice1"
$testservicename2 = "testservice2"
$svcbinaryname = "TestService"
$svcbinaryname = "TestService"
$svccmd = Get-Command $svcbinaryname
$svccmd | Should -Not -BeNullOrEmpty
$svcfullpath = $svccmd.Path
Expand Down Expand Up @@ -72,12 +76,34 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
@{
script = {Set-Service foo -StartupType bar -ErrorAction Stop};
errorid = "CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SetServiceCommand"
},
@{
script = {Set-Service -Name $testservicename1 -SecurityDescriptorSddl $WrongSecurityDescriptorSddl };
errorid = "System.ArgumentException,Microsoft.PowerShell.Commands.SetServiceCommand"
}
) {
param($script, $errorid)
{ & $script } | Should -Throw -ErrorId $errorid
}


It "Sets securitydescriptor of service using Set-Service " {
Set-Service -Name $TestServiceName1 -SecurityDescriptor $SecurityDescriptorSddl
$Counter = 0
$ExpectedSDDL = ConvertFrom-SddlString -Sddl $SecurityDescriptorSddl

# Selecting the first item in the output array as below command gives plain text output from the native sc.exe.
$UpdatedSDDL = ConvertFrom-SddlString -Sddl (sc sdshow $TestServiceName1)[1]

$UpdatedSDDL.Owner | Should -Be $ExpectedSDDL.Owner
$UpdatedSDDL.Group | Should -Be $ExpectedSDDL.Group
$UpdatedSDDL.DiscretionaryAcl.Count | Should -Be $ExpectedSDDL.DiscretionaryAcl.Count
$UpdatedSDDL.DiscretionaryAcl | ForEach-Object -Process {
$_ | Should -Be $ExpectedSDDL.DiscretionaryAcl[$Counter]
$Counter++
}
}

It "Set-Service can change '<parameter>' to '<value>'" -TestCases @(
@{parameter = "Description"; value = "hello"},
@{parameter = "DisplayName"; value = "test spooler"},
Expand Down Expand Up @@ -142,12 +168,12 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
try {
$startUsername = "user1"
$endUsername = "user2"
$testPass = "Secret123!"
$servicename = "testsetcredential"
net user $startUsername $testPass /add > $null
net user $endUsername $testPass /add > $null
$password = ConvertTo-SecureString $testPass -AsPlainText -Force
$creds = [pscredential]::new(".\$startUsername", $password)
$Password = ([char[]]([char]33..[char]95) + ([char[]]([char]97..[char]126)) + 0..9 | Sort-Object {Get-Random})[0..12] -join ''
$testPass = (New-Object -TypeName Net.NetworkCredential("", $Password)).SecurePassword
$creds = [pscredential]::new(".\$endUsername", $testPass)
net user $startUsername $creds.GetNetworkCredential().Password /add > $null
net user $endUsername $creds.GetNetworkCredential().Password /add > $null
$parameters = @{
Name = $servicename;
BinaryPathName = "$PSHOME\pwsh.exe";
Expand All @@ -159,7 +185,6 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
$service = Get-CimInstance Win32_Service -Filter "name='$servicename'"
$service.StartName | Should -BeExactly $creds.UserName

$creds = [pscredential]::new(".\$endUsername", $password)
Set-Service -Name $servicename -Credential $creds
$service = Get-CimInstance Win32_Service -Filter "name='$servicename'"
$service.StartName | Should -BeExactly $creds.UserName
Expand Down