-
Notifications
You must be signed in to change notification settings - Fork 63
/
ServiceUserManagement.ps1
393 lines (344 loc) · 15.4 KB
/
ServiceUserManagement.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
<#
Copyright 2012 Aaron Jensen (Carbon C# code)
Copyright 2014 Cloudbase Solutions Srl (PowerShell script)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#>
$Source = @"
/*
Original sources available at: https://bitbucket.org/splatteredbits/carbon
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
namespace PSCarbon
{
public sealed class Lsa
{
// ReSharper disable InconsistentNaming
[StructLayout(LayoutKind.Sequential)]
internal struct LSA_UNICODE_STRING
{
internal LSA_UNICODE_STRING(string inputString)
{
if (inputString == null)
{
Buffer = IntPtr.Zero;
Length = 0;
MaximumLength = 0;
}
else
{
Buffer = Marshal.StringToHGlobalAuto(inputString);
Length = (ushort)(inputString.Length * UnicodeEncoding.CharSize);
MaximumLength = (ushort)((inputString.Length + 1) * UnicodeEncoding.CharSize);
}
}
internal ushort Length;
internal ushort MaximumLength;
internal IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
internal struct LSA_OBJECT_ATTRIBUTES
{
internal uint Length;
internal IntPtr RootDirectory;
internal LSA_UNICODE_STRING ObjectName;
internal uint Attributes;
internal IntPtr SecurityDescriptor;
internal IntPtr SecurityQualityOfService;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public int HighPart;
}
// ReSharper disable UnusedMember.Local
private const uint POLICY_VIEW_LOCAL_INFORMATION = 0x00000001;
private const uint POLICY_VIEW_AUDIT_INFORMATION = 0x00000002;
private const uint POLICY_GET_PRIVATE_INFORMATION = 0x00000004;
private const uint POLICY_TRUST_ADMIN = 0x00000008;
private const uint POLICY_CREATE_ACCOUNT = 0x00000010;
private const uint POLICY_CREATE_SECRET = 0x00000014;
private const uint POLICY_CREATE_PRIVILEGE = 0x00000040;
private const uint POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080;
private const uint POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100;
private const uint POLICY_AUDIT_LOG_ADMIN = 0x00000200;
private const uint POLICY_SERVER_ADMIN = 0x00000400;
private const uint POLICY_LOOKUP_NAMES = 0x00000800;
private const uint POLICY_NOTIFICATION = 0x00001000;
// ReSharper restore UnusedMember.Local
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool LookupPrivilegeValue(
[MarshalAs(UnmanagedType.LPTStr)] string lpSystemName,
[MarshalAs(UnmanagedType.LPTStr)] string lpName,
out LUID lpLuid);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
private static extern uint LsaAddAccountRights(
IntPtr PolicyHandle,
IntPtr AccountSid,
LSA_UNICODE_STRING[] UserRights,
uint CountOfRights);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
private static extern uint LsaClose(IntPtr ObjectHandle);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern uint LsaEnumerateAccountRights(IntPtr PolicyHandle,
IntPtr AccountSid,
out IntPtr UserRights,
out uint CountOfRights
);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern uint LsaFreeMemory(IntPtr pBuffer);
[DllImport("advapi32.dll")]
private static extern int LsaNtStatusToWinError(long status);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
private static extern uint LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, uint DesiredAccess, out IntPtr PolicyHandle );
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
static extern uint LsaRemoveAccountRights(
IntPtr PolicyHandle,
IntPtr AccountSid,
[MarshalAs(UnmanagedType.U1)]
bool AllRights,
LSA_UNICODE_STRING[] UserRights,
uint CountOfRights);
// ReSharper restore InconsistentNaming
private static IntPtr GetIdentitySid(string identity)
{
var sid =
new NTAccount(identity).Translate(typeof (SecurityIdentifier)) as SecurityIdentifier;
if (sid == null)
{
throw new ArgumentException(string.Format("Account {0} not found.", identity));
}
var sidBytes = new byte[sid.BinaryLength];
sid.GetBinaryForm(sidBytes, 0);
var sidPtr = Marshal.AllocHGlobal(sidBytes.Length);
Marshal.Copy(sidBytes, 0, sidPtr, sidBytes.Length);
return sidPtr;
}
private static IntPtr GetLsaPolicyHandle()
{
var computerName = Environment.MachineName;
IntPtr hPolicy;
var objectAttributes = new LSA_OBJECT_ATTRIBUTES
{
Length = 0,
RootDirectory = IntPtr.Zero,
Attributes = 0,
SecurityDescriptor = IntPtr.Zero,
SecurityQualityOfService = IntPtr.Zero
};
const uint ACCESS_MASK = POLICY_CREATE_SECRET | POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION;
var machineNameLsa = new LSA_UNICODE_STRING(computerName);
var result = LsaOpenPolicy(ref machineNameLsa, ref objectAttributes, ACCESS_MASK, out hPolicy);
HandleLsaResult(result);
return hPolicy;
}
public static string[] GetPrivileges(string identity)
{
var sidPtr = GetIdentitySid(identity);
var hPolicy = GetLsaPolicyHandle();
var rightsPtr = IntPtr.Zero;
try
{
var privileges = new List<string>();
uint rightsCount;
var result = LsaEnumerateAccountRights(hPolicy, sidPtr, out rightsPtr, out rightsCount);
var win32ErrorCode = LsaNtStatusToWinError(result);
// the user has no privileges
if( win32ErrorCode == STATUS_OBJECT_NAME_NOT_FOUND )
{
return new string[0];
}
HandleLsaResult(result);
var myLsaus = new LSA_UNICODE_STRING();
for (ulong i = 0; i < rightsCount; i++)
{
var itemAddr = new IntPtr(rightsPtr.ToInt64() + (long) (i*(ulong) Marshal.SizeOf(myLsaus)));
myLsaus = (LSA_UNICODE_STRING) Marshal.PtrToStructure(itemAddr, myLsaus.GetType());
var cvt = new char[myLsaus.Length/UnicodeEncoding.CharSize];
Marshal.Copy(myLsaus.Buffer, cvt, 0, myLsaus.Length/UnicodeEncoding.CharSize);
var thisRight = new string(cvt);
privileges.Add(thisRight);
}
return privileges.ToArray();
}
finally
{
Marshal.FreeHGlobal(sidPtr);
var result = LsaClose(hPolicy);
HandleLsaResult(result);
result = LsaFreeMemory(rightsPtr);
HandleLsaResult(result);
}
}
public static void GrantPrivileges(string identity, string[] privileges)
{
var sidPtr = GetIdentitySid(identity);
var hPolicy = GetLsaPolicyHandle();
try
{
var lsaPrivileges = StringsToLsaStrings(privileges);
var result = LsaAddAccountRights(hPolicy, sidPtr, lsaPrivileges, (uint)lsaPrivileges.Length);
HandleLsaResult(result);
}
finally
{
Marshal.FreeHGlobal(sidPtr);
var result = LsaClose(hPolicy);
HandleLsaResult(result);
}
}
const int STATUS_SUCCESS = 0x0;
const int STATUS_OBJECT_NAME_NOT_FOUND = 0x00000002;
const int STATUS_ACCESS_DENIED = 0x00000005;
const int STATUS_INVALID_HANDLE = 0x00000006;
const int STATUS_UNSUCCESSFUL = 0x0000001F;
const int STATUS_INVALID_PARAMETER = 0x00000057;
const int STATUS_NO_SUCH_PRIVILEGE = 0x00000521;
const int STATUS_INVALID_SERVER_STATE = 0x00000548;
const int STATUS_INTERNAL_DB_ERROR = 0x00000567;
const int STATUS_INSUFFICIENT_RESOURCES = 0x000005AA;
private static readonly Dictionary<int, string> ErrorMessages = new Dictionary<int, string>
{
{STATUS_OBJECT_NAME_NOT_FOUND, "Object name not found. An object in the LSA policy database was not found. The object may have been specified either by SID or by name, depending on its type."},
{STATUS_ACCESS_DENIED, "Access denied. Caller does not have the appropriate access to complete the operation."},
{STATUS_INVALID_HANDLE, "Invalid handle. Indicates an object or RPC handle is not valid in the context used."},
{STATUS_UNSUCCESSFUL, "Unsuccessful. Generic failure, such as RPC connection failure."},
{STATUS_INVALID_PARAMETER, "Invalid parameter. One of the parameters is not valid."},
{STATUS_NO_SUCH_PRIVILEGE, "No such privilege. Indicates a specified privilege does not exist."},
{STATUS_INVALID_SERVER_STATE, "Invalid server state. Indicates the LSA server is currently disabled."},
{STATUS_INTERNAL_DB_ERROR, "Internal database error. The LSA database contains an internal inconsistency."},
{STATUS_INSUFFICIENT_RESOURCES, "Insufficient resources. There are not enough system resources (such as memory to allocate buffers) to complete the call."}
};
private static void HandleLsaResult(uint returnCode)
{
var win32ErrorCode = LsaNtStatusToWinError(returnCode);
if( win32ErrorCode == STATUS_SUCCESS)
return;
if( ErrorMessages.ContainsKey(win32ErrorCode) )
{
throw new Win32Exception(win32ErrorCode, ErrorMessages[win32ErrorCode]);
}
throw new Win32Exception(win32ErrorCode);
}
public static void RevokePrivileges(string identity, string[] privileges)
{
var sidPtr = GetIdentitySid(identity);
var hPolicy = GetLsaPolicyHandle();
try
{
var currentPrivileges = GetPrivileges(identity);
if (currentPrivileges.Length == 0)
{
return;
}
var lsaPrivileges = StringsToLsaStrings(privileges);
var result = LsaRemoveAccountRights(hPolicy, sidPtr, false, lsaPrivileges, (uint)lsaPrivileges.Length);
HandleLsaResult(result);
}
finally
{
Marshal.FreeHGlobal(sidPtr);
var result = LsaClose(hPolicy);
HandleLsaResult(result);
}
}
private static LSA_UNICODE_STRING[] StringsToLsaStrings(string[] privileges)
{
var lsaPrivileges = new LSA_UNICODE_STRING[privileges.Length];
for (var idx = 0; idx < privileges.Length; ++idx)
{
lsaPrivileges[idx] = new LSA_UNICODE_STRING(privileges[idx]);
}
return lsaPrivileges;
}
}
}
"@
Add-Type -TypeDefinition $Source -Language CSharp
$ServiceChangeErrors = @{}
$ServiceChangeErrors.Add(1, "Not Supported")
$ServiceChangeErrors.Add(2, "Access Denied")
$ServiceChangeErrors.Add(3, "Dependent Services Running")
$ServiceChangeErrors.Add(4, "Invalid Service Control")
$ServiceChangeErrors.Add(5, "Service Cannot Accept Control")
$ServiceChangeErrors.Add(6, "Service Not Active")
$ServiceChangeErrors.Add(7, "Service Request Timeout")
$ServiceChangeErrors.Add(8, "Unknown Failure")
$ServiceChangeErrors.Add(9, "Path Not Found")
$ServiceChangeErrors.Add(10, "Service Already Running")
$ServiceChangeErrors.Add(11, "Service Database Locked")
$ServiceChangeErrors.Add(12, "Service Dependency Deleted")
$ServiceChangeErrors.Add(13, "Service Dependency Failure")
$ServiceChangeErrors.Add(14, "Service Disabled")
$ServiceChangeErrors.Add(15, "Service Logon Failure")
$ServiceChangeErrors.Add(16, "Service Marked For Deletion")
$ServiceChangeErrors.Add(17, "Service No Thread")
$ServiceChangeErrors.Add(18, "Status Circular Dependency")
$ServiceChangeErrors.Add(19, "Status Duplicate Name")
$ServiceChangeErrors.Add(20, "Status Invalid Name")
$ServiceChangeErrors.Add(21, "Status Invalid Parameter")
$ServiceChangeErrors.Add(22, "Status Invalid Service Account")
$ServiceChangeErrors.Add(23, "Status Service Exists")
$ServiceChangeErrors.Add(24, "Service Already Paused")
function SetUserLogonAsServiceRights($UserName)
{
$privilege = "SeServiceLogonRight"
if (![PSCarbon.Lsa]::GetPrivileges($UserName).Contains($privilege))
{
[PSCarbon.Lsa]::GrantPrivileges($UserName, $privilege)
}
}
function Set-ServiceLogonCredentials
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]$ServiceName,
[parameter(Mandatory=$true)]
[PSCredential]$Credentials
)
PROCESS
{
$service = gwmi -class "Win32_Service" -Filter "Name = '$ServiceName'"
if(!$service)
{
throw "Service $ServiceName not found"
}
try
{
SetUserLogonAsServiceRights $Credentials.UserName
}
catch
{
throw "Failed to set SeServiceLogonRight rights for user '$($Credentials.UserName)'. " +
"Ensure that the user exists and that you have the proper access rights"
}
$serviceUserName = $Credentials.UserName
if (!$serviceUserName.Contains("\") -or $serviceUserName.Contains("@"))
{
$serviceUserName = ".\$serviceUserName"
}
$mbo = $service.Change($null, $null, $null, $null, $null, $null,
$serviceUserName,
$Credentials.GetNetworkCredential().Password)
if ($mbo.ReturnValue)
{
throw "Setting the service credentials failed with error: " +
$ServiceChangeErrors[$mbo.ReturnValue]
}
}
}