summaryrefslogtreecommitdiff
path: root/src/ext/Util/ca/scauser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Util/ca/scauser.cpp')
-rw-r--r--src/ext/Util/ca/scauser.cpp709
1 files changed, 709 insertions, 0 deletions
diff --git a/src/ext/Util/ca/scauser.cpp b/src/ext/Util/ca/scauser.cpp
new file mode 100644
index 00000000..b25e9daf
--- /dev/null
+++ b/src/ext/Util/ca/scauser.cpp
@@ -0,0 +1,709 @@
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
5LPCWSTR vcsUserQuery = L"SELECT `Wix4User`, `Component_`, `Name`, `Domain`, `Password` FROM `Wix4User` WHERE `Wix4User`=?";
6enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword };
7
8LPCWSTR vcsGroupQuery = L"SELECT `Wix4Group`, `Component_`, `Name`, `Domain` FROM `Wix4Group` WHERE `Wix4Group`=?";
9enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain };
10
11LPCWSTR vcsUserGroupQuery = L"SELECT `Wix4User_`, `Wix4Group_` FROM `Wix4UserGroup` WHERE `Wix4User_`=?";
12enum eUserGroupQuery { vugqUser = 1, vugqGroup };
13
14LPCWSTR vActionableQuery = L"SELECT `Wix4User`,`Component_`,`Name`,`Domain`,`Password`,`Attributes` FROM `Wix4User` WHERE `Component_` IS NOT NULL";
15enum eActionableQuery { vaqUser = 1, vaqComponent, vaqName, vaqDomain, vaqPassword, vaqAttributes };
16
17
18static HRESULT AddUserToList(
19 __inout SCA_USER** ppsuList
20 );
21
22static HRESULT AddGroupToList(
23 __inout SCA_GROUP** ppsgList
24 );
25
26
27HRESULT __stdcall ScaGetUser(
28 __in LPCWSTR wzUser,
29 __out SCA_USER* pscau
30 )
31{
32 if (!wzUser || !pscau)
33 {
34 return E_INVALIDARG;
35 }
36
37 HRESULT hr = S_OK;
38 PMSIHANDLE hView, hRec;
39
40 LPWSTR pwzData = NULL;
41
42 // clear struct and bail right away if no user key was passed to search for
43 ::ZeroMemory(pscau, sizeof(*pscau));
44 if (!*wzUser)
45 {
46 ExitFunction1(hr = S_OK);
47 }
48
49 hRec = ::MsiCreateRecord(1);
50 hr = WcaSetRecordString(hRec, 1, wzUser);
51 ExitOnFailure(hr, "Failed to look up User");
52
53 hr = WcaOpenView(vcsUserQuery, &hView);
54 ExitOnFailure(hr, "Failed to open view on Wix4User table");
55 hr = WcaExecuteView(hView, hRec);
56 ExitOnFailure(hr, "Failed to execute view on Wix4User table");
57
58 hr = WcaFetchSingleRecord(hView, &hRec);
59 if (S_OK == hr)
60 {
61 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
62 ExitOnFailure(hr, "Failed to get Wix4User.User");
63 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
64 ExitOnFailure(hr, "Failed to copy key string to user object");
65
66 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
67 ExitOnFailure(hr, "Failed to get Wix4User.Component_");
68 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
69 ExitOnFailure(hr, "Failed to copy component string to user object");
70
71 hr = WcaGetRecordFormattedString(hRec, vuqName, &pwzData);
72 ExitOnFailure(hr, "Failed to get Wix4User.Name");
73 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
74 ExitOnFailure(hr, "Failed to copy name string to user object");
75
76 hr = WcaGetRecordFormattedString(hRec, vuqDomain, &pwzData);
77 ExitOnFailure(hr, "Failed to get Wix4User.Domain");
78 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
79 ExitOnFailure(hr, "Failed to copy domain string to user object");
80
81 hr = WcaGetRecordFormattedString(hRec, vuqPassword, &pwzData);
82 ExitOnFailure(hr, "Failed to get Wix4User.Password");
83 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
84 ExitOnFailure(hr, "Failed to copy password string to user object");
85 }
86 else if (E_NOMOREITEMS == hr)
87 {
88 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4User.User='%ls'", wzUser);
89 hr = E_FAIL;
90 }
91 else
92 {
93 ExitOnFailure(hr, "Error or found multiple matching Wix4User rows");
94 }
95
96LExit:
97 ReleaseStr(pwzData);
98
99 return hr;
100}
101
102HRESULT __stdcall ScaGetUserDeferred(
103 __in LPCWSTR wzUser,
104 __in WCA_WRAPQUERY_HANDLE hUserQuery,
105 __out SCA_USER* pscau
106 )
107{
108 if (!wzUser || !pscau)
109 {
110 return E_INVALIDARG;
111 }
112
113 HRESULT hr = S_OK;
114 MSIHANDLE hRec, hRecTest;
115
116 LPWSTR pwzData = NULL;
117
118 // clear struct and bail right away if no user key was passed to search for
119 ::ZeroMemory(pscau, sizeof(*pscau));
120 if (!*wzUser)
121 {
122 ExitFunction1(hr = S_OK);
123 }
124
125 // Reset back to the first record
126 WcaFetchWrappedReset(hUserQuery);
127
128 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRec);
129 if (S_OK == hr)
130 {
131 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRecTest);
132 if (S_OK == hr)
133 {
134 AssertSz(FALSE, "Found multiple matching Wix4User rows");
135 }
136
137 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
138 ExitOnFailure(hr, "Failed to get Wix4User.User");
139 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
140 ExitOnFailure(hr, "Failed to copy key string to user object (in deferred CA)");
141
142 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
143 ExitOnFailure(hr, "Failed to get Wix4User.Component_");
144 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
145 ExitOnFailure(hr, "Failed to copy component string to user object (in deferred CA)");
146
147 hr = WcaGetRecordString(hRec, vuqName, &pwzData);
148 ExitOnFailure(hr, "Failed to get Wix4User.Name");
149 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
150 ExitOnFailure(hr, "Failed to copy name string to user object (in deferred CA)");
151
152 hr = WcaGetRecordString(hRec, vuqDomain, &pwzData);
153 ExitOnFailure(hr, "Failed to get Wix4User.Domain");
154 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
155 ExitOnFailure(hr, "Failed to copy domain string to user object (in deferred CA)");
156
157 hr = WcaGetRecordString(hRec, vuqPassword, &pwzData);
158 ExitOnFailure(hr, "Failed to get Wix4User.Password");
159 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
160 ExitOnFailure(hr, "Failed to copy password string to user object (in deferred CA)");
161 }
162 else if (E_NOMOREITEMS == hr)
163 {
164 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4User.User='%ls'", wzUser);
165 hr = E_FAIL;
166 }
167 else
168 {
169 ExitOnFailure(hr, "Error fetching single Wix4User row");
170 }
171
172LExit:
173 ReleaseStr(pwzData);
174
175 return hr;
176}
177
178
179HRESULT __stdcall ScaGetGroup(
180 __in LPCWSTR wzGroup,
181 __out SCA_GROUP* pscag
182 )
183{
184 if (!wzGroup || !pscag)
185 {
186 return E_INVALIDARG;
187 }
188
189 HRESULT hr = S_OK;
190 PMSIHANDLE hView, hRec;
191
192 LPWSTR pwzData = NULL;
193
194 hRec = ::MsiCreateRecord(1);
195 hr = WcaSetRecordString(hRec, 1, wzGroup);
196 ExitOnFailure(hr, "Failed to look up Group");
197
198 hr = WcaOpenView(vcsGroupQuery, &hView);
199 ExitOnFailure(hr, "Failed to open view on Wix4Group table");
200 hr = WcaExecuteView(hView, hRec);
201 ExitOnFailure(hr, "Failed to execute view on Wix4Group table");
202
203 hr = WcaFetchSingleRecord(hView, &hRec);
204 if (S_OK == hr)
205 {
206 hr = WcaGetRecordString(hRec, vgqGroup, &pwzData);
207 ExitOnFailure(hr, "Failed to get Wix4Group.Wix4Group.");
208 hr = ::StringCchCopyW(pscag->wzKey, countof(pscag->wzKey), pwzData);
209 ExitOnFailure(hr, "Failed to copy Wix4Group.Wix4Group.");
210
211 hr = WcaGetRecordString(hRec, vgqComponent, &pwzData);
212 ExitOnFailure(hr, "Failed to get Wix4Group.Component_");
213 hr = ::StringCchCopyW(pscag->wzComponent, countof(pscag->wzComponent), pwzData);
214 ExitOnFailure(hr, "Failed to copy Wix4Group.Component_.");
215
216 hr = WcaGetRecordFormattedString(hRec, vgqName, &pwzData);
217 ExitOnFailure(hr, "Failed to get Wix4Group.Name");
218 hr = ::StringCchCopyW(pscag->wzName, countof(pscag->wzName), pwzData);
219 ExitOnFailure(hr, "Failed to copy Wix4Group.Name.");
220
221 hr = WcaGetRecordFormattedString(hRec, vgqDomain, &pwzData);
222 ExitOnFailure(hr, "Failed to get Wix4Group.Domain");
223 hr = ::StringCchCopyW(pscag->wzDomain, countof(pscag->wzDomain), pwzData);
224 ExitOnFailure(hr, "Failed to copy Wix4Group.Domain.");
225 }
226 else if (E_NOMOREITEMS == hr)
227 {
228 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4Group.Wix4Group='%ls'", wzGroup);
229 hr = E_FAIL;
230 }
231 else
232 {
233 ExitOnFailure(hr, "Error or found multiple matching Wix4Group rows");
234 }
235
236LExit:
237 ReleaseStr(pwzData);
238
239 return hr;
240}
241
242
243void ScaUserFreeList(
244 __in SCA_USER* psuList
245 )
246{
247 SCA_USER* psuDelete = psuList;
248 while (psuList)
249 {
250 psuDelete = psuList;
251 psuList = psuList->psuNext;
252
253 ScaGroupFreeList(psuDelete->psgGroups);
254 MemFree(psuDelete);
255 }
256}
257
258
259void ScaGroupFreeList(
260 __in SCA_GROUP* psgList
261 )
262{
263 SCA_GROUP* psgDelete = psgList;
264 while (psgList)
265 {
266 psgDelete = psgList;
267 psgList = psgList->psgNext;
268
269 MemFree(psgDelete);
270 }
271}
272
273
274HRESULT ScaUserRead(
275 __out SCA_USER** ppsuList
276 )
277{
278 //Assert(FALSE);
279 Assert(ppsuList);
280
281 HRESULT hr = S_OK;
282 UINT er = ERROR_SUCCESS;
283 PMSIHANDLE hView, hRec, hUserRec, hUserGroupView;
284
285 LPWSTR pwzData = NULL;
286
287 BOOL fUserGroupExists = FALSE;
288
289 SCA_USER *psu = NULL;
290
291 INSTALLSTATE isInstalled, isAction;
292
293 if (S_OK != WcaTableExists(L"Wix4User"))
294 {
295 WcaLog(LOGMSG_VERBOSE, "Wix4User Table does not exist, exiting");
296 ExitFunction1(hr = S_FALSE);
297 }
298
299 if (S_OK == WcaTableExists(L"Wix4UserGroup"))
300 {
301 fUserGroupExists = TRUE;
302 }
303
304 //
305 // loop through all the users
306 //
307 hr = WcaOpenExecuteView(vActionableQuery, &hView);
308 ExitOnFailure(hr, "failed to open view on Wix4User table");
309 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
310 {
311 hr = WcaGetRecordString(hRec, vaqComponent, &pwzData);
312 ExitOnFailure(hr, "failed to get Wix4User.Component");
313
314 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction);
315 hr = HRESULT_FROM_WIN32(er);
316 ExitOnFailure(hr, "failed to get Component state for Wix4User");
317
318 // don't bother if we aren't installing or uninstalling this component
319 if (WcaIsInstalling(isInstalled, isAction) || WcaIsUninstalling(isInstalled, isAction))
320 {
321 //
322 // Add the user to the list and populate it's values
323 //
324 hr = AddUserToList(ppsuList);
325 ExitOnFailure(hr, "failed to add user to list");
326
327 psu = *ppsuList;
328
329 psu->isInstalled = isInstalled;
330 psu->isAction = isAction;
331 hr = ::StringCchCopyW(psu->wzComponent, countof(psu->wzComponent), pwzData);
332 ExitOnFailure(hr, "failed to copy component name: %ls", pwzData);
333
334 hr = WcaGetRecordString(hRec, vaqUser, &pwzData);
335 ExitOnFailure(hr, "failed to get Wix4User.User");
336 hr = ::StringCchCopyW(psu->wzKey, countof(psu->wzKey), pwzData);
337 ExitOnFailure(hr, "failed to copy user key: %ls", pwzData);
338
339 hr = WcaGetRecordFormattedString(hRec, vaqName, &pwzData);
340 ExitOnFailure(hr, "failed to get Wix4User.Name");
341 hr = ::StringCchCopyW(psu->wzName, countof(psu->wzName), pwzData);
342 ExitOnFailure(hr, "failed to copy user name: %ls", pwzData);
343
344 hr = WcaGetRecordFormattedString(hRec, vaqDomain, &pwzData);
345 ExitOnFailure(hr, "failed to get Wix4User.Domain");
346 hr = ::StringCchCopyW(psu->wzDomain, countof(psu->wzDomain), pwzData);
347 ExitOnFailure(hr, "failed to copy user domain: %ls", pwzData);
348
349 hr = WcaGetRecordFormattedString(hRec, vaqPassword, &pwzData);
350 ExitOnFailure(hr, "failed to get Wix4User.Password");
351 hr = ::StringCchCopyW(psu->wzPassword, countof(psu->wzPassword), pwzData);
352 ExitOnFailure(hr, "failed to copy user password");
353
354 hr = WcaGetRecordInteger(hRec, vaqAttributes, &psu->iAttributes);
355 ExitOnFailure(hr, "failed to get Wix4User.Attributes");
356
357 // Check if this user is to be added to any groups
358 if (fUserGroupExists)
359 {
360 hUserRec = ::MsiCreateRecord(1);
361 hr = WcaSetRecordString(hUserRec, 1, psu->wzKey);
362 ExitOnFailure(hr, "Failed to create user record for querying Wix4UserGroup table");
363
364 hr = WcaOpenView(vcsUserGroupQuery, &hUserGroupView);
365 ExitOnFailure(hr, "Failed to open view on Wix4UserGroup table for user %ls", psu->wzKey);
366 hr = WcaExecuteView(hUserGroupView, hUserRec);
367 ExitOnFailure(hr, "Failed to execute view on Wix4UserGroup table for user: %ls", psu->wzKey);
368
369 while (S_OK == (hr = WcaFetchRecord(hUserGroupView, &hRec)))
370 {
371 hr = WcaGetRecordString(hRec, vugqGroup, &pwzData);
372 ExitOnFailure(hr, "failed to get Wix4UserGroup.Group");
373
374 hr = AddGroupToList(&(psu->psgGroups));
375 ExitOnFailure(hr, "failed to add group to list");
376
377 hr = ScaGetGroup(pwzData, psu->psgGroups);
378 ExitOnFailure(hr, "failed to get information for group: %ls", pwzData);
379 }
380
381 if (E_NOMOREITEMS == hr)
382 {
383 hr = S_OK;
384 }
385 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4UserGroup table");
386 }
387 }
388 }
389
390 if (E_NOMOREITEMS == hr)
391 {
392 hr = S_OK;
393 }
394 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4User table");
395
396LExit:
397 ReleaseStr(pwzData);
398
399 return hr;
400}
401
402
403static HRESULT WriteGroupInfo(
404 __in SCA_GROUP* psgList,
405 __in LPWSTR *ppwzActionData
406 )
407{
408 HRESULT hr = S_OK;
409
410 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
411 {
412 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
413 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
414
415 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
416 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
417 }
418
419LExit:
420 return hr;
421}
422
423
424// Behaves like WriteGroupInfo, but it filters out groups the user is currently a member of,
425// because we don't want to rollback those
426static HRESULT WriteGroupRollbackInfo(
427 __in LPCWSTR pwzName,
428 __in LPCWSTR pwzDomain,
429 __in SCA_GROUP* psgList,
430 __in LPWSTR *ppwzActionData
431 )
432{
433 HRESULT hr = S_OK;
434 BOOL fIsMember = FALSE;
435
436 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
437 {
438 hr = UserCheckIsMember(pwzName, pwzDomain, psg->wzName, psg->wzDomain, &fIsMember);
439 if (FAILED(hr))
440 {
441 WcaLog(LOGMSG_VERBOSE, "Failed to check if user: %ls (domain: %ls) is member of a group while collecting rollback information (error code 0x%x) - continuing", pwzName, pwzDomain, hr);
442 hr = S_OK;
443 continue;
444 }
445
446 // If the user is currently a member, we don't want to undo that on rollback, so skip adding
447 // this group record to the list of groups to rollback
448 if (fIsMember)
449 {
450 continue;
451 }
452
453 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
454 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
455
456 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
457 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
458 }
459
460LExit:
461 return hr;
462}
463
464
465/* ****************************************************************
466ScaUserExecute - Schedules user account creation or removal based on
467component state.
468
469******************************************************************/
470HRESULT ScaUserExecute(
471 __in SCA_USER *psuList
472 )
473{
474 HRESULT hr = S_OK;
475 DWORD er = 0;
476 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
477
478 LPWSTR pwzBaseScriptKey = NULL;
479 DWORD cScriptKey = 0;
480
481 USER_INFO_0 *pUserInfo = NULL;
482 LPWSTR pwzScriptKey = NULL;
483 LPWSTR pwzActionData = NULL;
484 LPWSTR pwzRollbackData = NULL;
485
486 // Get the base script key for this CustomAction.
487 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
488 ExitOnFailure(hr, "Failed to get encoding key.");
489
490 // Loop through all the users to be configured.
491 for (SCA_USER *psu = psuList; psu; psu = psu->psuNext)
492 {
493 USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE;
494
495 // Always put the User Name and Domain plus Attributes on the front of the CustomAction
496 // data. Sometimes we'll add more data.
497 Assert(psu->wzName);
498 hr = WcaWriteStringToCaData(psu->wzName, &pwzActionData);
499 ExitOnFailure(hr, "Failed to add user name to custom action data: %ls", psu->wzName);
500 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzActionData);
501 ExitOnFailure(hr, "Failed to add user domain to custom action data: %ls", psu->wzDomain);
502 hr = WcaWriteIntegerToCaData(psu->iAttributes, &pwzActionData);
503 ExitOnFailure(hr, "failed to add user attributes to custom action data for user: %ls", psu->wzKey);
504
505 // Check to see if the user already exists since we have to be very careful when adding
506 // and removing users. Note: MSDN says that it is safe to call these APIs from any
507 // user, so we should be safe calling it during immediate mode.
508 er = ::NetApiBufferAllocate(sizeof(USER_INFO_0), reinterpret_cast<LPVOID*>(&pUserInfo));
509 hr = HRESULT_FROM_WIN32(er);
510 ExitOnFailure(hr, "Failed to allocate memory to check existence of user: %ls", psu->wzName);
511
512 LPCWSTR wzDomain = psu->wzDomain;
513 if (wzDomain && *wzDomain)
514 {
515 er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, NULL, &pDomainControllerInfo);
516 if (RPC_S_SERVER_UNAVAILABLE == er)
517 {
518 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
519 er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo);
520 }
521 if (ERROR_SUCCESS == er)
522 {
523 wzDomain = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
524 }
525 }
526
527 er = ::NetUserGetInfo(wzDomain, psu->wzName, 0, reinterpret_cast<LPBYTE*>(pUserInfo));
528 if (NERR_Success == er)
529 {
530 ueUserExists = USER_EXISTS_YES;
531 }
532 else if (NERR_UserNotFound == er)
533 {
534 ueUserExists = USER_EXISTS_NO;
535 }
536 else
537 {
538 ueUserExists = USER_EXISTS_INDETERMINATE;
539 hr = HRESULT_FROM_WIN32(er);
540 WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", wzDomain, psu->wzName, hr);
541 hr = S_OK;
542 er = ERROR_SUCCESS;
543 }
544
545 if (WcaIsInstalling(psu->isInstalled, psu->isAction))
546 {
547 // If the user exists, check to see if we are supposed to fail if user the exists before
548 // the install.
549 if (USER_EXISTS_YES == ueUserExists)
550 {
551 // Reinstalls will always fail if we don't remove the check for "fail if exists".
552 if (WcaIsReInstalling(psu->isInstalled, psu->isAction))
553 {
554 psu->iAttributes &= ~SCAU_FAIL_IF_EXISTS;
555 }
556
557 if ((SCAU_FAIL_IF_EXISTS & (psu->iAttributes)) && !(SCAU_UPDATE_IF_EXISTS & (psu->iAttributes)))
558 {
559 hr = HRESULT_FROM_WIN32(NERR_UserExists);
560 MessageExitOnFailure(hr, msierrUSRFailedUserCreateExists, "Failed to create user: %ls because user already exists.", psu->wzName);
561 }
562 }
563
564 // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user
565 if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER))
566 {
567 ++cScriptKey;
568 hr = StrAllocFormatted(&pwzScriptKey, L"%ls%u", pwzBaseScriptKey, cScriptKey);
569 ExitOnFailure(hr, "Failed to create encoding key.");
570
571 // Write the script key to CustomActionData for install and rollback so information can be passed to rollback.
572 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
573 ExitOnFailure(hr, "Failed to add encoding key to custom action data.");
574
575 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzRollbackData);
576 ExitOnFailure(hr, "Failed to add encoding key to rollback custom action data.");
577
578 INT iRollbackUserAttributes = psu->iAttributes;
579
580 // If the user already exists, ensure this is accounted for in rollback
581 if (USER_EXISTS_YES == ueUserExists)
582 {
583 iRollbackUserAttributes |= SCAU_DONT_CREATE_USER;
584 }
585 else
586 {
587 iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER;
588 }
589
590 // The deferred CA determines when to rollback User Rights Assignments so these should never be set.
591 iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_SERVICE;
592 iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_BATCH;
593
594 hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData);
595 ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName);
596 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData);
597 ExitOnFailure(hr, "Failed to add user domain to rollback custom action data: %ls", psu->wzDomain);
598 hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData);
599 ExitOnFailure(hr, "failed to add user attributes to rollback custom action data for user: %ls", psu->wzKey);
600
601 // If the user already exists, add relevant group information to rollback data
602 if (USER_EXISTS_YES == ueUserExists || USER_EXISTS_INDETERMINATE == ueUserExists)
603 {
604 hr = WriteGroupRollbackInfo(psu->wzName, psu->wzDomain, psu->psgGroups, &pwzRollbackData);
605 ExitOnFailure(hr, "failed to add group information to rollback custom action data");
606 }
607
608 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE);
609 ExitOnFailure(hr, "failed to schedule CreateUserRollback");
610 }
611 else
612 {
613 // Write empty script key to CustomActionData since there is no rollback.
614 hr = WcaWriteStringToCaData(L"", &pwzActionData);
615 ExitOnFailure(hr, "Failed to add empty encoding key to custom action data.");
616 }
617
618 //
619 // Schedule the creation now.
620 //
621 hr = WcaWriteStringToCaData(psu->wzPassword, &pwzActionData);
622 ExitOnFailure(hr, "failed to add user password to custom action data for user: %ls", psu->wzKey);
623
624 // Add user's group information to custom action data
625 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
626 ExitOnFailure(hr, "failed to add group information to custom action data");
627
628 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUser"), pwzActionData, COST_USER_ADD);
629 ExitOnFailure(hr, "failed to schedule CreateUser");
630 }
631 else if (((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists)) && WcaIsUninstalling(psu->isInstalled, psu->isAction) && !(psu->iAttributes & SCAU_DONT_REMOVE_ON_UNINSTALL))
632 {
633 // Add user's group information - this will ensure the user can be removed from any groups they were added to, if the user isn't be deleted
634 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
635 ExitOnFailure(hr, "failed to add group information to custom action data");
636
637 //
638 // Schedule the removal because the user exists and we don't have any flags set
639 // that say, don't remove the user on uninstall.
640 //
641 // Note: We can't rollback the removal of a user which is why RemoveUser is a commit
642 // CustomAction.
643 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RemoveUser"), pwzActionData, COST_USER_DELETE);
644 ExitOnFailure(hr, "failed to schedule RemoveUser");
645 }
646
647 ReleaseNullStr(pwzScriptKey);
648 ReleaseNullStr(pwzActionData);
649 ReleaseNullStr(pwzRollbackData);
650 if (pUserInfo)
651 {
652 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
653 pUserInfo = NULL;
654 }
655 if (pDomainControllerInfo)
656 {
657 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
658 pDomainControllerInfo = NULL;
659 }
660 }
661
662LExit:
663 ReleaseStr(pwzBaseScriptKey);
664 ReleaseStr(pwzScriptKey);
665 ReleaseStr(pwzActionData);
666 ReleaseStr(pwzRollbackData);
667 if (pUserInfo)
668 {
669 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
670 }
671 if (pDomainControllerInfo)
672 {
673 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
674 }
675
676 return hr;
677}
678
679
680static HRESULT AddUserToList(
681 __inout SCA_USER** ppsuList
682 )
683{
684 HRESULT hr = S_OK;
685 SCA_USER* psu = static_cast<SCA_USER*>(MemAlloc(sizeof(SCA_USER), TRUE));
686 ExitOnNull(psu, hr, E_OUTOFMEMORY, "failed to allocate memory for new user list element");
687
688 psu->psuNext = *ppsuList;
689 *ppsuList = psu;
690
691LExit:
692 return hr;
693}
694
695
696static HRESULT AddGroupToList(
697 __inout SCA_GROUP** ppsgList
698 )
699{
700 HRESULT hr = S_OK;
701 SCA_GROUP* psg = static_cast<SCA_GROUP*>(MemAlloc(sizeof(SCA_GROUP), TRUE));
702 ExitOnNull(psg, hr, E_OUTOFMEMORY, "failed to allocate memory for new group list element");
703
704 psg->psgNext = *ppsgList;
705 *ppsgList = psg;
706
707LExit:
708 return hr;
709}