summaryrefslogtreecommitdiff
path: root/src/ext/Util/ca/scaexec.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Util/ca/scaexec.cpp')
-rw-r--r--src/ext/Util/ca/scaexec.cpp1082
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 * ****************************************************************/
13extern "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
90LExit:
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 * ****************************************************************/
116extern "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
146LExit:
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
158static 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
221LExit:
222 ReleaseObject(pGroup);
223 ReleaseBSTR(bstrUser);
224 ReleaseBSTR(bstrGroup);
225
226 return hr;
227}
228
229static 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
287LExit:
288 ReleaseObject(pGroup);
289 ReleaseBSTR(bstrUser);
290 ReleaseBSTR(bstrGroup);
291
292 return hr;
293}
294
295
296static 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
328LExit:
329 if (rgSids)
330 {
331 ::LsaFreeMemory(rgSids);
332 }
333
334 return hr;
335}
336
337
338static 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
388LExit:
389 if (hPolicy)
390 {
391 ::LsaClose(hPolicy);
392 }
393
394 ReleaseSid(psid);
395 ReleaseStr(pwzUser);
396 return hr;
397}
398
399
400static 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
450LExit:
451 if (hPolicy)
452 {
453 ::LsaClose(hPolicy);
454 }
455
456 ReleaseSid(psid);
457 ReleaseStr(pwzUser);
458 return hr;
459}
460
461
462static 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
523static 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
573static 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
674LExit:
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 * *****************************************************************/
689extern "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
871LExit:
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 * *****************************************************************/
914extern "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
995LExit:
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 * *****************************************************************/
1023extern "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
1066LExit:
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}