diff options
Diffstat (limited to 'src/ext/Util/ca/scaexec.cpp')
-rw-r--r-- | src/ext/Util/ca/scaexec.cpp | 1082 |
1 files changed, 1082 insertions, 0 deletions
diff --git a/src/ext/Util/ca/scaexec.cpp b/src/ext/Util/ca/scaexec.cpp new file mode 100644 index 00000000..5845c1b4 --- /dev/null +++ b/src/ext/Util/ca/scaexec.cpp | |||
@@ -0,0 +1,1082 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | |||
6 | /******************************************************************** | ||
7 | * CreateSmb - CUSTOM ACTION ENTRY POINT for creating fileshares | ||
8 | * | ||
9 | * Input: deferred CustomActionData - | ||
10 | * wzFsKey\twzShareDesc\twzFullPath\tfIntegratedAuth\twzUserName\tnPermissions\twzUserName\tnPermissions... | ||
11 | * | ||
12 | * ****************************************************************/ | ||
13 | extern "C" UINT __stdcall CreateSmb(MSIHANDLE hInstall) | ||
14 | { | ||
15 | //AssertSz(0, "debug CreateSmb"); | ||
16 | UINT er = ERROR_SUCCESS; | ||
17 | HRESULT hr = S_OK; | ||
18 | |||
19 | LPWSTR pwzData = NULL; | ||
20 | LPWSTR pwz = NULL; | ||
21 | LPWSTR pwzFsKey = NULL; | ||
22 | LPWSTR pwzShareDesc = NULL; | ||
23 | LPWSTR pwzDirectory = NULL; | ||
24 | int iAccessMode = 0; | ||
25 | DWORD nExPermissions = 0; | ||
26 | BOOL fIntegratedAuth; | ||
27 | LPWSTR pwzExUser = NULL; | ||
28 | SCA_SMBP ssp = {0}; | ||
29 | DWORD dwExUserPerms = 0; | ||
30 | DWORD dwCounter = 0; | ||
31 | SCA_SMBP_USER_PERMS* pUserPermsList = NULL; | ||
32 | |||
33 | hr = WcaInitialize(hInstall, "CreateSmb"); | ||
34 | ExitOnFailure(hr, "failed to initialize"); | ||
35 | |||
36 | hr = WcaGetProperty( L"CustomActionData", &pwzData); | ||
37 | ExitOnFailure(hr, "failed to get CustomActionData"); | ||
38 | |||
39 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); | ||
40 | |||
41 | pwz = pwzData; | ||
42 | hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name | ||
43 | ExitOnFailure(hr, "failed to read share name"); | ||
44 | hr = WcaReadStringFromCaData(&pwz, &pwzShareDesc); // share description | ||
45 | ExitOnFailure(hr, "failed to read share name"); | ||
46 | hr = WcaReadStringFromCaData(&pwz, &pwzDirectory); // full path to share | ||
47 | ExitOnFailure(hr, "failed to read share name"); | ||
48 | hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fIntegratedAuth)); | ||
49 | ExitOnFailure(hr, "failed to read integrated authentication"); | ||
50 | |||
51 | hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&dwExUserPerms)); | ||
52 | ExitOnFailure(hr, "failed to read count of permissions to set"); | ||
53 | if(dwExUserPerms > 0) | ||
54 | { | ||
55 | pUserPermsList = static_cast<SCA_SMBP_USER_PERMS*>(MemAlloc(sizeof(SCA_SMBP_USER_PERMS)*dwExUserPerms, TRUE)); | ||
56 | ExitOnNull(pUserPermsList, hr, E_OUTOFMEMORY, "failed to allocate memory for permissions structure"); | ||
57 | |||
58 | //Pull out all of the ExUserPerm strings | ||
59 | for (dwCounter = 0; dwCounter < dwExUserPerms; ++dwCounter) | ||
60 | { | ||
61 | hr = WcaReadStringFromCaData(&pwz, &pwzExUser); // user account | ||
62 | ExitOnFailure(hr, "failed to read user account"); | ||
63 | pUserPermsList[dwCounter].wzUser = pwzExUser; | ||
64 | pwzExUser = NULL; | ||
65 | |||
66 | hr = WcaReadIntegerFromCaData(&pwz, &iAccessMode); | ||
67 | ExitOnFailure(hr, "failed to read access mode"); | ||
68 | pUserPermsList[dwCounter].accessMode = (ACCESS_MODE)iAccessMode; | ||
69 | iAccessMode = 0; | ||
70 | |||
71 | hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&nExPermissions)); | ||
72 | ExitOnFailure(hr, "failed to read count of permissions"); | ||
73 | pUserPermsList[dwCounter].nPermissions = nExPermissions; | ||
74 | nExPermissions = 0; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | ssp.wzKey = pwzFsKey; | ||
79 | ssp.wzDescription = pwzShareDesc; | ||
80 | ssp.wzDirectory = pwzDirectory; | ||
81 | ssp.fUseIntegratedAuth = fIntegratedAuth; | ||
82 | ssp.dwUserPermissionCount = dwExUserPerms; | ||
83 | ssp.pUserPerms = pUserPermsList; | ||
84 | |||
85 | hr = ScaEnsureSmbExists(&ssp); | ||
86 | MessageExitOnFailure(hr, msierrSMBFailedCreate, "failed to create share: '%ls'", pwzFsKey); | ||
87 | |||
88 | hr = WcaProgressMessage(COST_SMB_CREATESMB, FALSE); | ||
89 | |||
90 | LExit: | ||
91 | ReleaseStr(pwzFsKey); | ||
92 | ReleaseStr(pwzShareDesc); | ||
93 | ReleaseStr(pwzDirectory); | ||
94 | ReleaseStr(pwzData); | ||
95 | |||
96 | if (pUserPermsList) | ||
97 | { | ||
98 | MemFree(pUserPermsList); | ||
99 | } | ||
100 | |||
101 | if (FAILED(hr)) | ||
102 | { | ||
103 | er = ERROR_INSTALL_FAILURE; | ||
104 | } | ||
105 | return WcaFinalize(er); | ||
106 | } | ||
107 | |||
108 | |||
109 | |||
110 | /******************************************************************** | ||
111 | DropSmb - CUSTOM ACTION ENTRY POINT for creating fileshares | ||
112 | |||
113 | Input: deferred CustomActionData - wzFsKey\twzShareDesc\twzFullPath\tnPermissions\tfIntegratedAuth\twzUserName\twzPassword | ||
114 | |||
115 | * ****************************************************************/ | ||
116 | extern "C" UINT __stdcall DropSmb(MSIHANDLE hInstall) | ||
117 | { | ||
118 | //AssertSz(0, "debug DropSmb"); | ||
119 | UINT er = ERROR_SUCCESS; | ||
120 | HRESULT hr = S_OK; | ||
121 | |||
122 | LPWSTR pwzData = NULL; | ||
123 | LPWSTR pwz = NULL; | ||
124 | LPWSTR pwzFsKey = NULL; | ||
125 | SCA_SMBP ssp = {0}; | ||
126 | |||
127 | hr = WcaInitialize(hInstall, "DropSmb"); | ||
128 | ExitOnFailure(hr, "failed to initialize"); | ||
129 | |||
130 | hr = WcaGetProperty( L"CustomActionData", &pwzData); | ||
131 | ExitOnFailure(hr, "failed to get CustomActionData"); | ||
132 | |||
133 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); | ||
134 | |||
135 | pwz = pwzData; | ||
136 | hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name | ||
137 | ExitOnFailure(hr, "failed to read share name"); | ||
138 | |||
139 | ssp.wzKey = pwzFsKey; | ||
140 | |||
141 | hr = ScaDropSmb(&ssp); | ||
142 | MessageExitOnFailure(hr, msierrSMBFailedDrop, "failed to delete share: '%ls'", pwzFsKey); | ||
143 | |||
144 | hr = WcaProgressMessage(COST_SMB_DROPSMB, FALSE); | ||
145 | |||
146 | LExit: | ||
147 | ReleaseStr(pwzFsKey); | ||
148 | ReleaseStr(pwzData); | ||
149 | |||
150 | if (FAILED(hr)) | ||
151 | { | ||
152 | er = ERROR_INSTALL_FAILURE; | ||
153 | } | ||
154 | return WcaFinalize(er); | ||
155 | } | ||
156 | |||
157 | |||
158 | static HRESULT AddUserToGroup( | ||
159 | __in LPWSTR wzUser, | ||
160 | __in LPCWSTR wzUserDomain, | ||
161 | __in LPCWSTR wzGroup, | ||
162 | __in LPCWSTR wzGroupDomain | ||
163 | ) | ||
164 | { | ||
165 | Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain); | ||
166 | |||
167 | HRESULT hr = S_OK; | ||
168 | IADsGroup *pGroup = NULL; | ||
169 | BSTR bstrUser = NULL; | ||
170 | BSTR bstrGroup = NULL; | ||
171 | LPCWSTR wz = NULL; | ||
172 | LPWSTR pwzUser = NULL; | ||
173 | LOCALGROUP_MEMBERS_INFO_3 lgmi; | ||
174 | |||
175 | if (*wzGroupDomain) | ||
176 | { | ||
177 | wz = wzGroupDomain; | ||
178 | } | ||
179 | |||
180 | // Try adding it to the global group first | ||
181 | UINT ui = ::NetGroupAddUser(wz, wzGroup, wzUser); | ||
182 | if (NERR_GroupNotFound == ui) | ||
183 | { | ||
184 | // Try adding it to the local group | ||
185 | if (wzUserDomain) | ||
186 | { | ||
187 | hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser); | ||
188 | ExitOnFailure(hr, "failed to allocate user domain string"); | ||
189 | } | ||
190 | |||
191 | lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser); | ||
192 | ui = ::NetLocalGroupAddMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1); | ||
193 | } | ||
194 | hr = HRESULT_FROM_WIN32(ui); | ||
195 | if (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr) // if they're already a member of the group don't report an error | ||
196 | hr = S_OK; | ||
197 | |||
198 | // | ||
199 | // If we failed, try active directory | ||
200 | // | ||
201 | if (FAILED(hr)) | ||
202 | { | ||
203 | WcaLog(LOGMSG_VERBOSE, "Failed to add user: %ls, domain %ls to group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr); | ||
204 | |||
205 | hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser); | ||
206 | ExitOnFailure(hr, "failed to create user ADsPath for user: %ls domain: %ls", wzUser, wzUserDomain); | ||
207 | |||
208 | hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup); | ||
209 | ExitOnFailure(hr, "failed to create group ADsPath for group: %ls domain: %ls", wzGroup, wzGroupDomain); | ||
210 | |||
211 | hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup)); | ||
212 | ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) ); | ||
213 | |||
214 | hr = pGroup->Add(bstrUser); | ||
215 | if ((HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) == hr) || (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr)) | ||
216 | hr = S_OK; | ||
217 | |||
218 | ExitOnFailure(hr, "Failed to add user %ls to group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) ); | ||
219 | } | ||
220 | |||
221 | LExit: | ||
222 | ReleaseObject(pGroup); | ||
223 | ReleaseBSTR(bstrUser); | ||
224 | ReleaseBSTR(bstrGroup); | ||
225 | |||
226 | return hr; | ||
227 | } | ||
228 | |||
229 | static HRESULT RemoveUserFromGroup( | ||
230 | __in LPWSTR wzUser, | ||
231 | __in LPCWSTR wzUserDomain, | ||
232 | __in LPCWSTR wzGroup, | ||
233 | __in LPCWSTR wzGroupDomain | ||
234 | ) | ||
235 | { | ||
236 | Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain); | ||
237 | |||
238 | HRESULT hr = S_OK; | ||
239 | IADsGroup *pGroup = NULL; | ||
240 | BSTR bstrUser = NULL; | ||
241 | BSTR bstrGroup = NULL; | ||
242 | LPCWSTR wz = NULL; | ||
243 | LPWSTR pwzUser = NULL; | ||
244 | LOCALGROUP_MEMBERS_INFO_3 lgmi; | ||
245 | |||
246 | if (*wzGroupDomain) | ||
247 | { | ||
248 | wz = wzGroupDomain; | ||
249 | } | ||
250 | |||
251 | // Try removing it from the global group first | ||
252 | UINT ui = ::NetGroupDelUser(wz, wzGroup, wzUser); | ||
253 | if (NERR_GroupNotFound == ui) | ||
254 | { | ||
255 | // Try removing it from the local group | ||
256 | if (wzUserDomain) | ||
257 | { | ||
258 | hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser); | ||
259 | ExitOnFailure(hr, "failed to allocate user domain string"); | ||
260 | } | ||
261 | |||
262 | lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser); | ||
263 | ui = ::NetLocalGroupDelMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1); | ||
264 | } | ||
265 | hr = HRESULT_FROM_WIN32(ui); | ||
266 | |||
267 | // | ||
268 | // If we failed, try active directory | ||
269 | // | ||
270 | if (FAILED(hr)) | ||
271 | { | ||
272 | WcaLog(LOGMSG_VERBOSE, "Failed to remove user: %ls, domain %ls from group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr); | ||
273 | |||
274 | hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser); | ||
275 | ExitOnFailure(hr, "failed to create user ADsPath in order to remove user: %ls domain: %ls from a group", wzUser, wzUserDomain); | ||
276 | |||
277 | hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup); | ||
278 | ExitOnFailure(hr, "failed to create group ADsPath in order to remove user from group: %ls domain: %ls", wzGroup, wzGroupDomain); | ||
279 | |||
280 | hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup)); | ||
281 | ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) ); | ||
282 | |||
283 | hr = pGroup->Remove(bstrUser); | ||
284 | ExitOnFailure(hr, "Failed to remove user %ls from group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) ); | ||
285 | } | ||
286 | |||
287 | LExit: | ||
288 | ReleaseObject(pGroup); | ||
289 | ReleaseBSTR(bstrUser); | ||
290 | ReleaseBSTR(bstrGroup); | ||
291 | |||
292 | return hr; | ||
293 | } | ||
294 | |||
295 | |||
296 | static HRESULT GetUserHasRight( | ||
297 | __in LSA_HANDLE hPolicy, | ||
298 | __in PSID pUserSid, | ||
299 | __in LPWSTR wzRight, | ||
300 | __out BOOL* fHasRight | ||
301 | ) | ||
302 | { | ||
303 | HRESULT hr = S_OK; | ||
304 | NTSTATUS nt = 0; | ||
305 | LSA_UNICODE_STRING lucPrivilege = { 0 }; | ||
306 | PLSA_ENUMERATION_INFORMATION rgSids = NULL; | ||
307 | ULONG cSids = 0; | ||
308 | *fHasRight = FALSE; | ||
309 | |||
310 | lucPrivilege.Buffer = wzRight; | ||
311 | lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR)); | ||
312 | lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR); | ||
313 | |||
314 | nt = ::LsaEnumerateAccountsWithUserRight(hPolicy, &lucPrivilege, reinterpret_cast<PVOID*>(&rgSids), &cSids); | ||
315 | hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); | ||
316 | ExitOnFailure(hr, "Failed to enumerate users for right: %ls", lucPrivilege.Buffer); | ||
317 | |||
318 | for (DWORD i = 0; i < cSids; ++i) | ||
319 | { | ||
320 | PLSA_ENUMERATION_INFORMATION pInfo = rgSids + i; | ||
321 | if (::EqualSid(pUserSid, pInfo->Sid)) | ||
322 | { | ||
323 | *fHasRight = TRUE; | ||
324 | break; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | LExit: | ||
329 | if (rgSids) | ||
330 | { | ||
331 | ::LsaFreeMemory(rgSids); | ||
332 | } | ||
333 | |||
334 | return hr; | ||
335 | } | ||
336 | |||
337 | |||
338 | static HRESULT GetExistingUserRightsAssignments( | ||
339 | __in_opt LPCWSTR wzDomain, | ||
340 | __in LPCWSTR wzName, | ||
341 | __inout int* iAttributes | ||
342 | ) | ||
343 | { | ||
344 | HRESULT hr = S_OK; | ||
345 | NTSTATUS nt = 0; | ||
346 | BOOL fHasRight = FALSE; | ||
347 | |||
348 | LSA_HANDLE hPolicy = NULL; | ||
349 | LSA_OBJECT_ATTRIBUTES objectAttributes = { 0 }; | ||
350 | |||
351 | LPWSTR pwzUser = NULL; | ||
352 | PSID psid = NULL; | ||
353 | |||
354 | if (wzDomain && *wzDomain) | ||
355 | { | ||
356 | hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName); | ||
357 | ExitOnFailure(hr, "Failed to allocate user with domain string"); | ||
358 | } | ||
359 | else | ||
360 | { | ||
361 | hr = StrAllocString(&pwzUser, wzName, 0); | ||
362 | ExitOnFailure(hr, "Failed to allocate string from user name."); | ||
363 | } | ||
364 | |||
365 | hr = AclGetAccountSid(NULL, pwzUser, &psid); | ||
366 | ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser); | ||
367 | |||
368 | nt = ::LsaOpenPolicy(NULL, &objectAttributes, POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, &hPolicy); | ||
369 | hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); | ||
370 | ExitOnFailure(hr, "Failed to open LSA policy store"); | ||
371 | |||
372 | hr = GetUserHasRight(hPolicy, psid, L"SeServiceLogonRight", &fHasRight); | ||
373 | ExitOnFailure(hr, "Failed to check LogonAsService right"); | ||
374 | |||
375 | if (fHasRight) | ||
376 | { | ||
377 | *iAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE; | ||
378 | } | ||
379 | |||
380 | hr = GetUserHasRight(hPolicy, psid, L"SeBatchLogonRight", &fHasRight); | ||
381 | ExitOnFailure(hr, "Failed to check LogonAsBatchJob right"); | ||
382 | |||
383 | if (fHasRight) | ||
384 | { | ||
385 | *iAttributes |= SCAU_ALLOW_LOGON_AS_BATCH; | ||
386 | } | ||
387 | |||
388 | LExit: | ||
389 | if (hPolicy) | ||
390 | { | ||
391 | ::LsaClose(hPolicy); | ||
392 | } | ||
393 | |||
394 | ReleaseSid(psid); | ||
395 | ReleaseStr(pwzUser); | ||
396 | return hr; | ||
397 | } | ||
398 | |||
399 | |||
400 | static HRESULT ModifyUserLocalServiceRight( | ||
401 | __in_opt LPCWSTR wzDomain, | ||
402 | __in LPCWSTR wzName, | ||
403 | __in BOOL fAdd | ||
404 | ) | ||
405 | { | ||
406 | HRESULT hr = S_OK; | ||
407 | NTSTATUS nt = 0; | ||
408 | |||
409 | LPWSTR pwzUser = NULL; | ||
410 | PSID psid = NULL; | ||
411 | LSA_HANDLE hPolicy = NULL; | ||
412 | LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 }; | ||
413 | LSA_UNICODE_STRING lucPrivilege = { 0 }; | ||
414 | |||
415 | if (wzDomain && *wzDomain) | ||
416 | { | ||
417 | hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName); | ||
418 | ExitOnFailure(hr, "Failed to allocate user with domain string"); | ||
419 | } | ||
420 | else | ||
421 | { | ||
422 | hr = StrAllocString(&pwzUser, wzName, 0); | ||
423 | ExitOnFailure(hr, "Failed to allocate string from user name."); | ||
424 | } | ||
425 | |||
426 | hr = AclGetAccountSid(NULL, pwzUser, &psid); | ||
427 | ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser); | ||
428 | |||
429 | nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy); | ||
430 | hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); | ||
431 | ExitOnFailure(hr, "Failed to open LSA policy store."); | ||
432 | |||
433 | lucPrivilege.Buffer = L"SeServiceLogonRight"; | ||
434 | lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR)); | ||
435 | lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR); | ||
436 | |||
437 | if (fAdd) | ||
438 | { | ||
439 | nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1); | ||
440 | hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); | ||
441 | ExitOnFailure(hr, "Failed to add 'logon as service' bit to user: %ls", pwzUser); | ||
442 | } | ||
443 | else | ||
444 | { | ||
445 | nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1); | ||
446 | hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); | ||
447 | ExitOnFailure(hr, "Failed to remove 'logon as service' bit from user: %ls", pwzUser); | ||
448 | } | ||
449 | |||
450 | LExit: | ||
451 | if (hPolicy) | ||
452 | { | ||
453 | ::LsaClose(hPolicy); | ||
454 | } | ||
455 | |||
456 | ReleaseSid(psid); | ||
457 | ReleaseStr(pwzUser); | ||
458 | return hr; | ||
459 | } | ||
460 | |||
461 | |||
462 | static HRESULT ModifyUserLocalBatchRight( | ||
463 | __in_opt LPCWSTR wzDomain, | ||
464 | __in LPCWSTR wzName, | ||
465 | __in BOOL fAdd | ||
466 | ) | ||
467 | { | ||
468 | HRESULT hr = S_OK; | ||
469 | NTSTATUS nt = 0; | ||
470 | |||
471 | LPWSTR pwzUser = NULL; | ||
472 | PSID psid = NULL; | ||
473 | LSA_HANDLE hPolicy = NULL; | ||
474 | LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 }; | ||
475 | LSA_UNICODE_STRING lucPrivilege = { 0 }; | ||
476 | |||
477 | if (wzDomain && *wzDomain) | ||
478 | { | ||
479 | hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName); | ||
480 | ExitOnFailure(hr, "Failed to allocate user with domain string"); | ||
481 | } | ||
482 | else | ||
483 | { | ||
484 | hr = StrAllocString(&pwzUser, wzName, 0); | ||
485 | ExitOnFailure(hr, "Failed to allocate string from user name."); | ||
486 | } | ||
487 | |||
488 | hr = AclGetAccountSid(NULL, pwzUser, &psid); | ||
489 | ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser); | ||
490 | |||
491 | nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy); | ||
492 | hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); | ||
493 | ExitOnFailure(hr, "Failed to open LSA policy store."); | ||
494 | |||
495 | lucPrivilege.Buffer = L"SeBatchLogonRight"; | ||
496 | lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR)); | ||
497 | lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR); | ||
498 | |||
499 | if (fAdd) | ||
500 | { | ||
501 | nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1); | ||
502 | hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); | ||
503 | ExitOnFailure(hr, "Failed to add 'logon as batch job' bit to user: %ls", pwzUser); | ||
504 | } | ||
505 | else | ||
506 | { | ||
507 | nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1); | ||
508 | hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); | ||
509 | ExitOnFailure(hr, "Failed to remove 'logon as batch job' bit from user: %ls", pwzUser); | ||
510 | } | ||
511 | |||
512 | LExit: | ||
513 | if (hPolicy) | ||
514 | { | ||
515 | ::LsaClose(hPolicy); | ||
516 | } | ||
517 | |||
518 | ReleaseSid(psid); | ||
519 | ReleaseStr(pwzUser); | ||
520 | return hr; | ||
521 | } | ||
522 | |||
523 | static void SetUserPasswordAndAttributes( | ||
524 | __in USER_INFO_1* puserInfo, | ||
525 | __in LPWSTR wzPassword, | ||
526 | __in int iAttributes | ||
527 | ) | ||
528 | { | ||
529 | Assert(puserInfo); | ||
530 | |||
531 | // Set the User's password | ||
532 | puserInfo->usri1_password = wzPassword; | ||
533 | |||
534 | // Apply the Attributes | ||
535 | if (SCAU_DONT_EXPIRE_PASSWRD & iAttributes) | ||
536 | { | ||
537 | puserInfo->usri1_flags |= UF_DONT_EXPIRE_PASSWD; | ||
538 | } | ||
539 | else | ||
540 | { | ||
541 | puserInfo->usri1_flags &= ~UF_DONT_EXPIRE_PASSWD; | ||
542 | } | ||
543 | |||
544 | if (SCAU_PASSWD_CANT_CHANGE & iAttributes) | ||
545 | { | ||
546 | puserInfo->usri1_flags |= UF_PASSWD_CANT_CHANGE; | ||
547 | } | ||
548 | else | ||
549 | { | ||
550 | puserInfo->usri1_flags &= ~UF_PASSWD_CANT_CHANGE; | ||
551 | } | ||
552 | |||
553 | if (SCAU_DISABLE_ACCOUNT & iAttributes) | ||
554 | { | ||
555 | puserInfo->usri1_flags |= UF_ACCOUNTDISABLE; | ||
556 | } | ||
557 | else | ||
558 | { | ||
559 | puserInfo->usri1_flags &= ~UF_ACCOUNTDISABLE; | ||
560 | } | ||
561 | |||
562 | if (SCAU_PASSWD_CHANGE_REQD_ON_LOGIN & iAttributes) // TODO: for some reason this doesn't work | ||
563 | { | ||
564 | puserInfo->usri1_flags |= UF_PASSWORD_EXPIRED; | ||
565 | } | ||
566 | else | ||
567 | { | ||
568 | puserInfo->usri1_flags &= ~UF_PASSWORD_EXPIRED; | ||
569 | } | ||
570 | } | ||
571 | |||
572 | |||
573 | static HRESULT RemoveUserInternal( | ||
574 | LPWSTR wzGroupCaData, | ||
575 | LPWSTR wzDomain, | ||
576 | LPWSTR wzName, | ||
577 | int iAttributes | ||
578 | ) | ||
579 | { | ||
580 | HRESULT hr = S_OK; | ||
581 | UINT er = ERROR_SUCCESS; | ||
582 | |||
583 | LPWSTR pwz = NULL; | ||
584 | LPWSTR pwzGroup = NULL; | ||
585 | LPWSTR pwzGroupDomain = NULL; | ||
586 | LPCWSTR wz = NULL; | ||
587 | PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; | ||
588 | |||
589 | // | ||
590 | // Remove the logon as service privilege. | ||
591 | // | ||
592 | if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes) | ||
593 | { | ||
594 | hr = ModifyUserLocalServiceRight(wzDomain, wzName, FALSE); | ||
595 | if (FAILED(hr)) | ||
596 | { | ||
597 | WcaLogError(hr, "Failed to remove logon as service right from user, continuing..."); | ||
598 | hr = S_OK; | ||
599 | } | ||
600 | } | ||
601 | |||
602 | if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes) | ||
603 | { | ||
604 | hr = ModifyUserLocalBatchRight(wzDomain, wzName, FALSE); | ||
605 | if (FAILED(hr)) | ||
606 | { | ||
607 | WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing..."); | ||
608 | hr = S_OK; | ||
609 | } | ||
610 | } | ||
611 | |||
612 | // | ||
613 | // Remove the User Account if the user was created by us. | ||
614 | // | ||
615 | if (!(SCAU_DONT_CREATE_USER & iAttributes)) | ||
616 | { | ||
617 | if (wzDomain && *wzDomain) | ||
618 | { | ||
619 | er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, NULL, &pDomainControllerInfo); | ||
620 | if (RPC_S_SERVER_UNAVAILABLE == er) | ||
621 | { | ||
622 | // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag | ||
623 | er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo); | ||
624 | } | ||
625 | if (ERROR_SUCCESS == er) | ||
626 | { | ||
627 | wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix | ||
628 | } | ||
629 | else | ||
630 | { | ||
631 | wz = wzDomain; | ||
632 | } | ||
633 | } | ||
634 | |||
635 | er = ::NetUserDel(wz, wzName); | ||
636 | if (NERR_UserNotFound == er) | ||
637 | { | ||
638 | er = NERR_Success; | ||
639 | } | ||
640 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to delete user account: %ls", wzName); | ||
641 | } | ||
642 | else | ||
643 | { | ||
644 | // | ||
645 | // Remove the user from the groups | ||
646 | // | ||
647 | pwz = wzGroupCaData; | ||
648 | while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup))) | ||
649 | { | ||
650 | hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain); | ||
651 | |||
652 | if (FAILED(hr)) | ||
653 | { | ||
654 | WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup); | ||
655 | } | ||
656 | else | ||
657 | { | ||
658 | hr = RemoveUserFromGroup(wzName, wzDomain, pwzGroup, pwzGroupDomain); | ||
659 | if (FAILED(hr)) | ||
660 | { | ||
661 | WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", wzName, pwzGroup); | ||
662 | } | ||
663 | } | ||
664 | } | ||
665 | |||
666 | if (E_NOMOREITEMS == hr) // if there are no more items, all is well | ||
667 | { | ||
668 | hr = S_OK; | ||
669 | } | ||
670 | |||
671 | ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", wzName); | ||
672 | } | ||
673 | |||
674 | LExit: | ||
675 | if (pDomainControllerInfo) | ||
676 | { | ||
677 | ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo)); | ||
678 | } | ||
679 | |||
680 | return hr; | ||
681 | } | ||
682 | |||
683 | |||
684 | /******************************************************************** | ||
685 | CreateUser - CUSTOM ACTION ENTRY POINT for creating users | ||
686 | |||
687 | Input: deferred CustomActionData - UserName\tDomain\tPassword\tAttributes\tGroupName\tDomain\tGroupName\tDomain... | ||
688 | * *****************************************************************/ | ||
689 | extern "C" UINT __stdcall CreateUser( | ||
690 | __in MSIHANDLE hInstall | ||
691 | ) | ||
692 | { | ||
693 | //AssertSz(0, "Debug CreateUser"); | ||
694 | |||
695 | HRESULT hr = S_OK; | ||
696 | UINT er = ERROR_SUCCESS; | ||
697 | |||
698 | LPWSTR pwzData = NULL; | ||
699 | LPWSTR pwz = NULL; | ||
700 | LPWSTR pwzName = NULL; | ||
701 | LPWSTR pwzDomain = NULL; | ||
702 | LPWSTR pwzScriptKey = NULL; | ||
703 | LPWSTR pwzPassword = NULL; | ||
704 | LPWSTR pwzGroup = NULL; | ||
705 | LPWSTR pwzGroupDomain = NULL; | ||
706 | PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; | ||
707 | int iAttributes = 0; | ||
708 | BOOL fInitializedCom = FALSE; | ||
709 | |||
710 | WCA_CASCRIPT_HANDLE hRollbackScript = NULL; | ||
711 | int iOriginalAttributes = 0; | ||
712 | int iRollbackAttributes = 0; | ||
713 | |||
714 | USER_INFO_1 userInfo; | ||
715 | USER_INFO_1* puserInfo = NULL; | ||
716 | DWORD dw; | ||
717 | LPCWSTR wz = NULL; | ||
718 | |||
719 | hr = WcaInitialize(hInstall, "CreateUser"); | ||
720 | ExitOnFailure(hr, "failed to initialize"); | ||
721 | |||
722 | hr = ::CoInitialize(NULL); | ||
723 | ExitOnFailure(hr, "failed to initialize COM"); | ||
724 | fInitializedCom = TRUE; | ||
725 | |||
726 | hr = WcaGetProperty( L"CustomActionData", &pwzData); | ||
727 | ExitOnFailure(hr, "failed to get CustomActionData"); | ||
728 | |||
729 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); | ||
730 | |||
731 | // | ||
732 | // Read in the CustomActionData | ||
733 | // | ||
734 | pwz = pwzData; | ||
735 | hr = WcaReadStringFromCaData(&pwz, &pwzName); | ||
736 | ExitOnFailure(hr, "failed to read user name from custom action data"); | ||
737 | |||
738 | hr = WcaReadStringFromCaData(&pwz, &pwzDomain); | ||
739 | ExitOnFailure(hr, "failed to read domain from custom action data"); | ||
740 | |||
741 | hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); | ||
742 | ExitOnFailure(hr, "failed to read attributes from custom action data"); | ||
743 | |||
744 | hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey); | ||
745 | ExitOnFailure(hr, "failed to read encoding key from custom action data"); | ||
746 | |||
747 | hr = WcaReadStringFromCaData(&pwz, &pwzPassword); | ||
748 | ExitOnFailure(hr, "failed to read password from custom action data"); | ||
749 | |||
750 | // There is no rollback scheduled if the key is empty. | ||
751 | // Best effort to get original configuration and save it in the script so rollback can restore it. | ||
752 | if (*pwzScriptKey) | ||
753 | { | ||
754 | hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript); | ||
755 | ExitOnFailure(hr, "Failed to open rollback CustomAction script."); | ||
756 | |||
757 | iRollbackAttributes = 0; | ||
758 | hr = GetExistingUserRightsAssignments(pwzDomain, pwzName, &iOriginalAttributes); | ||
759 | if (FAILED(hr)) | ||
760 | { | ||
761 | WcaLogError(hr, "failed to get existing user rights: %ls, continuing anyway.", pwzName); | ||
762 | } | ||
763 | else | ||
764 | { | ||
765 | if (!(SCAU_ALLOW_LOGON_AS_SERVICE & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)) | ||
766 | { | ||
767 | iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE; | ||
768 | } | ||
769 | if (!(SCAU_ALLOW_LOGON_AS_BATCH & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)) | ||
770 | { | ||
771 | iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_BATCH; | ||
772 | } | ||
773 | } | ||
774 | |||
775 | hr = WcaCaScriptWriteNumber(hRollbackScript, iRollbackAttributes); | ||
776 | ExitOnFailure(hr, "Failed to add data to rollback script."); | ||
777 | |||
778 | // Nudge the system to get all our rollback data written to disk. | ||
779 | WcaCaScriptFlush(hRollbackScript); | ||
780 | } | ||
781 | |||
782 | if (!(SCAU_DONT_CREATE_USER & iAttributes)) | ||
783 | { | ||
784 | ::ZeroMemory(&userInfo, sizeof(USER_INFO_1)); | ||
785 | userInfo.usri1_name = pwzName; | ||
786 | userInfo.usri1_priv = USER_PRIV_USER; | ||
787 | userInfo.usri1_flags = UF_SCRIPT; | ||
788 | userInfo.usri1_home_dir = NULL; | ||
789 | userInfo.usri1_comment = NULL; | ||
790 | userInfo.usri1_script_path = NULL; | ||
791 | |||
792 | SetUserPasswordAndAttributes(&userInfo, pwzPassword, iAttributes); | ||
793 | |||
794 | // | ||
795 | // Create the User | ||
796 | // | ||
797 | if (pwzDomain && *pwzDomain) | ||
798 | { | ||
799 | er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo ); | ||
800 | if (RPC_S_SERVER_UNAVAILABLE == er) | ||
801 | { | ||
802 | // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag | ||
803 | er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo ); | ||
804 | } | ||
805 | if (ERROR_SUCCESS == er) | ||
806 | { | ||
807 | wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix | ||
808 | } | ||
809 | else | ||
810 | { | ||
811 | wz = pwzDomain; | ||
812 | } | ||
813 | } | ||
814 | |||
815 | er = ::NetUserAdd(wz, 1, reinterpret_cast<LPBYTE>(&userInfo), &dw); | ||
816 | if (NERR_UserExists == er) | ||
817 | { | ||
818 | if (SCAU_UPDATE_IF_EXISTS & iAttributes) | ||
819 | { | ||
820 | er = ::NetUserGetInfo(wz, pwzName, 1, reinterpret_cast<LPBYTE*>(&puserInfo)); | ||
821 | if (NERR_Success == er) | ||
822 | { | ||
823 | // Change the existing user's password and attributes again then try | ||
824 | // to update user with this new data | ||
825 | SetUserPasswordAndAttributes(puserInfo, pwzPassword, iAttributes); | ||
826 | |||
827 | er = ::NetUserSetInfo(wz, pwzName, 1, reinterpret_cast<LPBYTE>(puserInfo), &dw); | ||
828 | } | ||
829 | } | ||
830 | else if (!(SCAU_FAIL_IF_EXISTS & iAttributes)) | ||
831 | { | ||
832 | er = NERR_Success; | ||
833 | } | ||
834 | } | ||
835 | else if (NERR_PasswordTooShort == er || NERR_PasswordTooLong == er) | ||
836 | { | ||
837 | MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreatePswd, "failed to create user: %ls due to invalid password.", pwzName); | ||
838 | } | ||
839 | MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreate, "failed to create user: %ls", pwzName); | ||
840 | } | ||
841 | |||
842 | if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes) | ||
843 | { | ||
844 | hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, TRUE); | ||
845 | MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as service rights to user: %ls", pwzName); | ||
846 | } | ||
847 | |||
848 | if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes) | ||
849 | { | ||
850 | hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, TRUE); | ||
851 | MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as batch job rights to user: %ls", pwzName); | ||
852 | } | ||
853 | |||
854 | // | ||
855 | // Add the users to groups | ||
856 | // | ||
857 | while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup))) | ||
858 | { | ||
859 | hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain); | ||
860 | ExitOnFailure(hr, "failed to get domain for group: %ls", pwzGroup); | ||
861 | |||
862 | hr = AddUserToGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain); | ||
863 | MessageExitOnFailure(hr, msierrUSRFailedUserGroupAdd, "failed to add user: %ls to group %ls", pwzName, pwzGroup); | ||
864 | } | ||
865 | if (E_NOMOREITEMS == hr) // if there are no more items, all is well | ||
866 | { | ||
867 | hr = S_OK; | ||
868 | } | ||
869 | ExitOnFailure(hr, "failed to get next group in which to include user:%ls", pwzName); | ||
870 | |||
871 | LExit: | ||
872 | WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE); | ||
873 | |||
874 | if (puserInfo) | ||
875 | { | ||
876 | ::NetApiBufferFree((LPVOID)puserInfo); | ||
877 | } | ||
878 | |||
879 | if (pDomainControllerInfo) | ||
880 | { | ||
881 | ::NetApiBufferFree((LPVOID)pDomainControllerInfo); | ||
882 | } | ||
883 | |||
884 | ReleaseStr(pwzData); | ||
885 | ReleaseStr(pwzName); | ||
886 | ReleaseStr(pwzDomain); | ||
887 | ReleaseStr(pwzScriptKey); | ||
888 | ReleaseStr(pwzPassword); | ||
889 | ReleaseStr(pwzGroup); | ||
890 | ReleaseStr(pwzGroupDomain); | ||
891 | |||
892 | if (fInitializedCom) | ||
893 | { | ||
894 | ::CoUninitialize(); | ||
895 | } | ||
896 | |||
897 | if (SCAU_NON_VITAL & iAttributes) | ||
898 | { | ||
899 | er = ERROR_SUCCESS; | ||
900 | } | ||
901 | else if (FAILED(hr)) | ||
902 | { | ||
903 | er = ERROR_INSTALL_FAILURE; | ||
904 | } | ||
905 | |||
906 | return WcaFinalize(er); | ||
907 | } | ||
908 | |||
909 | |||
910 | /******************************************************************** | ||
911 | CreateUserRollback - CUSTOM ACTION ENTRY POINT for CreateUser rollback | ||
912 | |||
913 | * *****************************************************************/ | ||
914 | extern "C" UINT __stdcall CreateUserRollback( | ||
915 | MSIHANDLE hInstall | ||
916 | ) | ||
917 | { | ||
918 | //AssertSz(0, "Debug CreateUserRollback"); | ||
919 | |||
920 | HRESULT hr = S_OK; | ||
921 | UINT er = ERROR_SUCCESS; | ||
922 | |||
923 | LPWSTR pwzData = NULL; | ||
924 | LPWSTR pwz = NULL; | ||
925 | LPWSTR pwzName = NULL; | ||
926 | LPWSTR pwzDomain = NULL; | ||
927 | LPWSTR pwzScriptKey = NULL; | ||
928 | int iAttributes = 0; | ||
929 | BOOL fInitializedCom = FALSE; | ||
930 | |||
931 | WCA_CASCRIPT_HANDLE hRollbackScript = NULL; | ||
932 | LPWSTR pwzRollbackData = NULL; | ||
933 | int iOriginalAttributes = 0; | ||
934 | |||
935 | hr = WcaInitialize(hInstall, "CreateUserRollback"); | ||
936 | ExitOnFailure(hr, "failed to initialize"); | ||
937 | |||
938 | hr = ::CoInitialize(NULL); | ||
939 | ExitOnFailure(hr, "failed to initialize COM"); | ||
940 | fInitializedCom = TRUE; | ||
941 | |||
942 | hr = WcaGetProperty(L"CustomActionData", &pwzData); | ||
943 | ExitOnFailure(hr, "failed to get CustomActionData"); | ||
944 | |||
945 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); | ||
946 | |||
947 | // | ||
948 | // Read in the CustomActionData | ||
949 | // | ||
950 | pwz = pwzData; | ||
951 | hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey); | ||
952 | ExitOnFailure(hr, "failed to read encoding key from custom action data"); | ||
953 | |||
954 | hr = WcaReadStringFromCaData(&pwz, &pwzName); | ||
955 | ExitOnFailure(hr, "failed to read name from custom action data"); | ||
956 | |||
957 | hr = WcaReadStringFromCaData(&pwz, &pwzDomain); | ||
958 | ExitOnFailure(hr, "failed to read domain from custom action data"); | ||
959 | |||
960 | hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); | ||
961 | ExitOnFailure(hr, "failed to read attributes from custom action data"); | ||
962 | |||
963 | // Best effort to read original configuration from CreateUser. | ||
964 | hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript); | ||
965 | if (FAILED(hr)) | ||
966 | { | ||
967 | WcaLogError(hr, "Failed to open rollback CustomAction script, continuing anyway."); | ||
968 | } | ||
969 | else | ||
970 | { | ||
971 | hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzRollbackData); | ||
972 | if (FAILED(hr)) | ||
973 | { | ||
974 | WcaLogError(hr, "Failed to read rollback script into CustomAction data, continuing anyway."); | ||
975 | } | ||
976 | else | ||
977 | { | ||
978 | WcaLog(LOGMSG_TRACEONLY, "Rollback Data: %ls", pwzRollbackData); | ||
979 | |||
980 | pwz = pwzRollbackData; | ||
981 | hr = WcaReadIntegerFromCaData(&pwz, &iOriginalAttributes); | ||
982 | if (FAILED(hr)) | ||
983 | { | ||
984 | WcaLogError(hr, "failed to read attributes from rollback data, continuing anyway"); | ||
985 | } | ||
986 | else | ||
987 | { | ||
988 | iAttributes |= iOriginalAttributes; | ||
989 | } | ||
990 | } | ||
991 | } | ||
992 | |||
993 | hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes); | ||
994 | |||
995 | LExit: | ||
996 | WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE); | ||
997 | |||
998 | ReleaseStr(pwzData); | ||
999 | ReleaseStr(pwzName); | ||
1000 | ReleaseStr(pwzDomain); | ||
1001 | ReleaseStr(pwzScriptKey); | ||
1002 | ReleaseStr(pwzRollbackData); | ||
1003 | |||
1004 | if (fInitializedCom) | ||
1005 | { | ||
1006 | ::CoUninitialize(); | ||
1007 | } | ||
1008 | |||
1009 | if (FAILED(hr)) | ||
1010 | { | ||
1011 | er = ERROR_INSTALL_FAILURE; | ||
1012 | } | ||
1013 | |||
1014 | return WcaFinalize(er); | ||
1015 | } | ||
1016 | |||
1017 | |||
1018 | /******************************************************************** | ||
1019 | RemoveUser - CUSTOM ACTION ENTRY POINT for removing users | ||
1020 | |||
1021 | Input: deferred CustomActionData - Name\tDomain | ||
1022 | * *****************************************************************/ | ||
1023 | extern "C" UINT __stdcall RemoveUser( | ||
1024 | MSIHANDLE hInstall | ||
1025 | ) | ||
1026 | { | ||
1027 | //AssertSz(0, "Debug RemoveUser"); | ||
1028 | |||
1029 | HRESULT hr = S_OK; | ||
1030 | UINT er = ERROR_SUCCESS; | ||
1031 | |||
1032 | LPWSTR pwzData = NULL; | ||
1033 | LPWSTR pwz = NULL; | ||
1034 | LPWSTR pwzName = NULL; | ||
1035 | LPWSTR pwzDomain = NULL; | ||
1036 | int iAttributes = 0; | ||
1037 | BOOL fInitializedCom = FALSE; | ||
1038 | |||
1039 | hr = WcaInitialize(hInstall, "RemoveUser"); | ||
1040 | ExitOnFailure(hr, "failed to initialize"); | ||
1041 | |||
1042 | hr = ::CoInitialize(NULL); | ||
1043 | ExitOnFailure(hr, "failed to initialize COM"); | ||
1044 | fInitializedCom = TRUE; | ||
1045 | |||
1046 | hr = WcaGetProperty(L"CustomActionData", &pwzData); | ||
1047 | ExitOnFailure(hr, "failed to get CustomActionData"); | ||
1048 | |||
1049 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); | ||
1050 | |||
1051 | // | ||
1052 | // Read in the CustomActionData | ||
1053 | // | ||
1054 | pwz = pwzData; | ||
1055 | hr = WcaReadStringFromCaData(&pwz, &pwzName); | ||
1056 | ExitOnFailure(hr, "failed to read name from custom action data"); | ||
1057 | |||
1058 | hr = WcaReadStringFromCaData(&pwz, &pwzDomain); | ||
1059 | ExitOnFailure(hr, "failed to read domain from custom action data"); | ||
1060 | |||
1061 | hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); | ||
1062 | ExitOnFailure(hr, "failed to read attributes from custom action data"); | ||
1063 | |||
1064 | hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes); | ||
1065 | |||
1066 | LExit: | ||
1067 | ReleaseStr(pwzData); | ||
1068 | ReleaseStr(pwzName); | ||
1069 | ReleaseStr(pwzDomain); | ||
1070 | |||
1071 | if (fInitializedCom) | ||
1072 | { | ||
1073 | ::CoUninitialize(); | ||
1074 | } | ||
1075 | |||
1076 | if (FAILED(hr)) | ||
1077 | { | ||
1078 | er = ERROR_INSTALL_FAILURE; | ||
1079 | } | ||
1080 | |||
1081 | return WcaFinalize(er); | ||
1082 | } | ||