diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2018-12-15 21:46:30 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-15 21:46:30 -0600 |
commit | f7020c0d16baf2b960e7123e233e20c519f6a340 (patch) | |
tree | d2cd464ee15b2b3f304ff780c531b39bb292d331 /src/ca/scauser.cpp | |
parent | 6ed8d107e6edf16956c778bda3573f8d7a7690fc (diff) | |
download | wix-f7020c0d16baf2b960e7123e233e20c519f6a340.tar.gz wix-f7020c0d16baf2b960e7123e233e20c519f6a340.tar.bz2 wix-f7020c0d16baf2b960e7123e233e20c519f6a340.zip |
Import implementation of UtilCA from old repo's WixCA/scasched/scaexec. (#3)
Diffstat (limited to 'src/ca/scauser.cpp')
-rw-r--r-- | src/ca/scauser.cpp | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/src/ca/scauser.cpp b/src/ca/scauser.cpp new file mode 100644 index 00000000..43317bdc --- /dev/null +++ b/src/ca/scauser.cpp | |||
@@ -0,0 +1,676 @@ | |||
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 | LPCWSTR vcsUserQuery = L"SELECT `User`, `Component_`, `Name`, `Domain`, `Password` FROM `User` WHERE `User`=?"; | ||
6 | enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword }; | ||
7 | |||
8 | LPCWSTR vcsGroupQuery = L"SELECT `Group`, `Component_`, `Name`, `Domain` FROM `Group` WHERE `Group`=?"; | ||
9 | enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain }; | ||
10 | |||
11 | LPCWSTR vcsUserGroupQuery = L"SELECT `User_`, `Group_` FROM `UserGroup` WHERE `User_`=?"; | ||
12 | enum eUserGroupQuery { vugqUser = 1, vugqGroup }; | ||
13 | |||
14 | LPCWSTR vActionableQuery = L"SELECT `User`,`Component_`,`Name`,`Domain`,`Password`,`Attributes` FROM `User` WHERE `Component_` IS NOT NULL"; | ||
15 | enum eActionableQuery { vaqUser = 1, vaqComponent, vaqName, vaqDomain, vaqPassword, vaqAttributes }; | ||
16 | |||
17 | |||
18 | static HRESULT AddUserToList( | ||
19 | __inout SCA_USER** ppsuList | ||
20 | ); | ||
21 | |||
22 | static HRESULT AddGroupToList( | ||
23 | __inout SCA_GROUP** ppsgList | ||
24 | ); | ||
25 | |||
26 | |||
27 | HRESULT __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 User table"); | ||
55 | hr = WcaExecuteView(hView, hRec); | ||
56 | ExitOnFailure(hr, "Failed to execute view on User 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 User.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 User.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 User.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 User.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 User.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 User.User='%ls'", wzUser); | ||
89 | hr = E_FAIL; | ||
90 | } | ||
91 | else | ||
92 | { | ||
93 | ExitOnFailure(hr, "Error or found multiple matching User rows"); | ||
94 | } | ||
95 | |||
96 | LExit: | ||
97 | ReleaseStr(pwzData); | ||
98 | |||
99 | return hr; | ||
100 | } | ||
101 | |||
102 | HRESULT __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 User rows"); | ||
135 | } | ||
136 | |||
137 | hr = WcaGetRecordString(hRec, vuqUser, &pwzData); | ||
138 | ExitOnFailure(hr, "Failed to get User.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 User.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 User.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 User.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 User.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 User.User='%ls'", wzUser); | ||
165 | hr = E_FAIL; | ||
166 | } | ||
167 | else | ||
168 | { | ||
169 | ExitOnFailure(hr, "Error fetching single User row"); | ||
170 | } | ||
171 | |||
172 | LExit: | ||
173 | ReleaseStr(pwzData); | ||
174 | |||
175 | return hr; | ||
176 | } | ||
177 | |||
178 | |||
179 | HRESULT __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 Group table"); | ||
200 | hr = WcaExecuteView(hView, hRec); | ||
201 | ExitOnFailure(hr, "Failed to execute view on Group 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 Group.Group"); | ||
208 | hr = ::StringCchCopyW(pscag->wzKey, countof(pscag->wzKey), pwzData); | ||
209 | ExitOnFailure(hr, "Failed to copy Group.Group."); | ||
210 | |||
211 | hr = WcaGetRecordString(hRec, vgqComponent, &pwzData); | ||
212 | ExitOnFailure(hr, "Failed to get Group.Component_"); | ||
213 | hr = ::StringCchCopyW(pscag->wzComponent, countof(pscag->wzComponent), pwzData); | ||
214 | ExitOnFailure(hr, "Failed to copy Group.Component_."); | ||
215 | |||
216 | hr = WcaGetRecordFormattedString(hRec, vgqName, &pwzData); | ||
217 | ExitOnFailure(hr, "Failed to get Group.Name"); | ||
218 | hr = ::StringCchCopyW(pscag->wzName, countof(pscag->wzName), pwzData); | ||
219 | ExitOnFailure(hr, "Failed to copy Group.Name."); | ||
220 | |||
221 | hr = WcaGetRecordFormattedString(hRec, vgqDomain, &pwzData); | ||
222 | ExitOnFailure(hr, "Failed to get Group.Domain"); | ||
223 | hr = ::StringCchCopyW(pscag->wzDomain, countof(pscag->wzDomain), pwzData); | ||
224 | ExitOnFailure(hr, "Failed to copy Group.Domain."); | ||
225 | } | ||
226 | else if (E_NOMOREITEMS == hr) | ||
227 | { | ||
228 | WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Group.Group='%ls'", wzGroup); | ||
229 | hr = E_FAIL; | ||
230 | } | ||
231 | else | ||
232 | { | ||
233 | ExitOnFailure(hr, "Error or found multiple matching Group rows"); | ||
234 | } | ||
235 | |||
236 | LExit: | ||
237 | ReleaseStr(pwzData); | ||
238 | |||
239 | return hr; | ||
240 | } | ||
241 | |||
242 | |||
243 | void 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 | |||
259 | void 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 | |||
274 | HRESULT 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"User")) | ||
294 | { | ||
295 | WcaLog(LOGMSG_VERBOSE, "User Table does not exist, exiting"); | ||
296 | ExitFunction1(hr = S_FALSE); | ||
297 | } | ||
298 | |||
299 | if (S_OK == WcaTableExists(L"UserGroup")) | ||
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 User table"); | ||
309 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
310 | { | ||
311 | hr = WcaGetRecordString(hRec, vaqComponent, &pwzData); | ||
312 | ExitOnFailure(hr, "failed to get User.Component"); | ||
313 | |||
314 | er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction); | ||
315 | hr = HRESULT_FROM_WIN32(er); | ||
316 | ExitOnFailure(hr, "failed to get Component state for User"); | ||
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 User.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 User.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 User.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 User.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 User.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 UserGroup table"); | ||
363 | |||
364 | hr = WcaOpenView(vcsUserGroupQuery, &hUserGroupView); | ||
365 | ExitOnFailure(hr, "Failed to open view on UserGroup table for user %ls", psu->wzKey); | ||
366 | hr = WcaExecuteView(hUserGroupView, hUserRec); | ||
367 | ExitOnFailure(hr, "Failed to execute view on UserGroup 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 UserGroup.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 UserGroup 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 User table"); | ||
395 | |||
396 | LExit: | ||
397 | ReleaseStr(pwzData); | ||
398 | |||
399 | return hr; | ||
400 | } | ||
401 | |||
402 | |||
403 | static 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 | |||
419 | LExit: | ||
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 | ||
426 | static 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 | |||
460 | LExit: | ||
461 | return hr; | ||
462 | } | ||
463 | |||
464 | |||
465 | /* **************************************************************** | ||
466 | ScaUserExecute - Schedules user account creation or removal based on | ||
467 | component state. | ||
468 | |||
469 | ******************************************************************/ | ||
470 | HRESULT 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 | USER_INFO_0 *pUserInfo = NULL; | ||
479 | LPWSTR pwzActionData = NULL; | ||
480 | LPWSTR pwzRollbackData = NULL; | ||
481 | |||
482 | for (SCA_USER *psu = psuList; psu; psu = psu->psuNext) | ||
483 | { | ||
484 | USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE; | ||
485 | |||
486 | // Always put the User Name and Domain plus Attributes on the front of the CustomAction | ||
487 | // data. Sometimes we'll add more data. | ||
488 | Assert(psu->wzName); | ||
489 | hr = WcaWriteStringToCaData(psu->wzName, &pwzActionData); | ||
490 | ExitOnFailure(hr, "Failed to add user name to custom action data: %ls", psu->wzName); | ||
491 | hr = WcaWriteStringToCaData(psu->wzDomain, &pwzActionData); | ||
492 | ExitOnFailure(hr, "Failed to add user domain to custom action data: %ls", psu->wzDomain); | ||
493 | hr = WcaWriteIntegerToCaData(psu->iAttributes, &pwzActionData); | ||
494 | ExitOnFailure(hr, "failed to add user attributes to custom action data for user: %ls", psu->wzKey); | ||
495 | |||
496 | // Check to see if the user already exists since we have to be very careful when adding | ||
497 | // and removing users. Note: MSDN says that it is safe to call these APIs from any | ||
498 | // user, so we should be safe calling it during immediate mode. | ||
499 | er = ::NetApiBufferAllocate(sizeof(USER_INFO_0), reinterpret_cast<LPVOID*>(&pUserInfo)); | ||
500 | hr = HRESULT_FROM_WIN32(er); | ||
501 | ExitOnFailure(hr, "Failed to allocate memory to check existence of user: %ls", psu->wzName); | ||
502 | |||
503 | LPCWSTR wzDomain = psu->wzDomain; | ||
504 | if (wzDomain && *wzDomain) | ||
505 | { | ||
506 | er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, NULL, &pDomainControllerInfo); | ||
507 | if (RPC_S_SERVER_UNAVAILABLE == er) | ||
508 | { | ||
509 | // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag | ||
510 | er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo); | ||
511 | } | ||
512 | if (ERROR_SUCCESS == er) | ||
513 | { | ||
514 | wzDomain = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix | ||
515 | } | ||
516 | } | ||
517 | |||
518 | er = ::NetUserGetInfo(wzDomain, psu->wzName, 0, reinterpret_cast<LPBYTE*>(pUserInfo)); | ||
519 | if (NERR_Success == er) | ||
520 | { | ||
521 | ueUserExists = USER_EXISTS_YES; | ||
522 | } | ||
523 | else if (NERR_UserNotFound == er) | ||
524 | { | ||
525 | ueUserExists = USER_EXISTS_NO; | ||
526 | } | ||
527 | else | ||
528 | { | ||
529 | ueUserExists = USER_EXISTS_INDETERMINATE; | ||
530 | hr = HRESULT_FROM_WIN32(er); | ||
531 | WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", wzDomain, psu->wzName, hr); | ||
532 | hr = S_OK; | ||
533 | er = ERROR_SUCCESS; | ||
534 | } | ||
535 | |||
536 | if (WcaIsInstalling(psu->isInstalled, psu->isAction)) | ||
537 | { | ||
538 | // If the user exists, check to see if we are supposed to fail if user the exists before | ||
539 | // the install. | ||
540 | if (USER_EXISTS_YES == ueUserExists) | ||
541 | { | ||
542 | // Reinstalls will always fail if we don't remove the check for "fail if exists". | ||
543 | if (WcaIsReInstalling(psu->isInstalled, psu->isAction)) | ||
544 | { | ||
545 | psu->iAttributes &= ~SCAU_FAIL_IF_EXISTS; | ||
546 | } | ||
547 | |||
548 | if ((SCAU_FAIL_IF_EXISTS & (psu->iAttributes)) && !(SCAU_UPDATE_IF_EXISTS & (psu->iAttributes))) | ||
549 | { | ||
550 | hr = HRESULT_FROM_WIN32(NERR_UserExists); | ||
551 | MessageExitOnFailure(hr, msierrUSRFailedUserCreateExists, "Failed to create user: %ls because user already exists.", psu->wzName); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user | ||
556 | if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER)) | ||
557 | { | ||
558 | INT iRollbackUserAttributes = psu->iAttributes; | ||
559 | |||
560 | // If the user already exists, ensure this is accounted for in rollback | ||
561 | if (USER_EXISTS_YES == ueUserExists) | ||
562 | { | ||
563 | iRollbackUserAttributes |= SCAU_DONT_CREATE_USER; | ||
564 | } | ||
565 | else | ||
566 | { | ||
567 | iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER; | ||
568 | } | ||
569 | |||
570 | hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData); | ||
571 | ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName); | ||
572 | hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData); | ||
573 | ExitOnFailure(hr, "Failed to add user domain to rollback custom action data: %ls", psu->wzDomain); | ||
574 | hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData); | ||
575 | ExitOnFailure(hr, "failed to add user attributes to rollback custom action data for user: %ls", psu->wzKey); | ||
576 | |||
577 | // If the user already exists, add relevant group information to rollback data | ||
578 | if (USER_EXISTS_YES == ueUserExists || USER_EXISTS_INDETERMINATE == ueUserExists) | ||
579 | { | ||
580 | hr = WriteGroupRollbackInfo(psu->wzName, psu->wzDomain, psu->psgGroups, &pwzRollbackData); | ||
581 | ExitOnFailure(hr, "failed to add group information to rollback custom action data"); | ||
582 | } | ||
583 | |||
584 | hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE); | ||
585 | ExitOnFailure(hr, "failed to schedule CreateUserRollback"); | ||
586 | } | ||
587 | |||
588 | // | ||
589 | // Schedule the creation now. | ||
590 | // | ||
591 | hr = WcaWriteStringToCaData(psu->wzPassword, &pwzActionData); | ||
592 | ExitOnFailure(hr, "failed to add user password to custom action data for user: %ls", psu->wzKey); | ||
593 | |||
594 | // Add user's group information to custom action data | ||
595 | hr = WriteGroupInfo(psu->psgGroups, &pwzActionData); | ||
596 | ExitOnFailure(hr, "failed to add group information to custom action data"); | ||
597 | |||
598 | hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"CreateUser"), pwzActionData, COST_USER_ADD); | ||
599 | ExitOnFailure(hr, "failed to schedule CreateUser"); | ||
600 | } | ||
601 | else if (((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists)) && WcaIsUninstalling(psu->isInstalled, psu->isAction) && !(psu->iAttributes & SCAU_DONT_REMOVE_ON_UNINSTALL)) | ||
602 | { | ||
603 | // 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 | ||
604 | hr = WriteGroupInfo(psu->psgGroups, &pwzActionData); | ||
605 | ExitOnFailure(hr, "failed to add group information to custom action data"); | ||
606 | |||
607 | // | ||
608 | // Schedule the removal because the user exists and we don't have any flags set | ||
609 | // that say, don't remove the user on uninstall. | ||
610 | // | ||
611 | // Note: We can't rollback the removal of a user which is why RemoveUser is a commit | ||
612 | // CustomAction. | ||
613 | hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RemoveUser"), pwzActionData, COST_USER_DELETE); | ||
614 | ExitOnFailure(hr, "failed to schedule RemoveUser"); | ||
615 | } | ||
616 | |||
617 | ReleaseNullStr(pwzActionData); | ||
618 | ReleaseNullStr(pwzRollbackData); | ||
619 | if (pUserInfo) | ||
620 | { | ||
621 | ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo)); | ||
622 | pUserInfo = NULL; | ||
623 | } | ||
624 | if (pDomainControllerInfo) | ||
625 | { | ||
626 | ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo)); | ||
627 | pDomainControllerInfo = NULL; | ||
628 | } | ||
629 | } | ||
630 | |||
631 | LExit: | ||
632 | ReleaseStr(pwzActionData); | ||
633 | ReleaseStr(pwzRollbackData); | ||
634 | if (pUserInfo) | ||
635 | { | ||
636 | ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo)); | ||
637 | } | ||
638 | if (pDomainControllerInfo) | ||
639 | { | ||
640 | ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo)); | ||
641 | } | ||
642 | |||
643 | return hr; | ||
644 | } | ||
645 | |||
646 | |||
647 | static HRESULT AddUserToList( | ||
648 | __inout SCA_USER** ppsuList | ||
649 | ) | ||
650 | { | ||
651 | HRESULT hr = S_OK; | ||
652 | SCA_USER* psu = static_cast<SCA_USER*>(MemAlloc(sizeof(SCA_USER), TRUE)); | ||
653 | ExitOnNull(psu, hr, E_OUTOFMEMORY, "failed to allocate memory for new user list element"); | ||
654 | |||
655 | psu->psuNext = *ppsuList; | ||
656 | *ppsuList = psu; | ||
657 | |||
658 | LExit: | ||
659 | return hr; | ||
660 | } | ||
661 | |||
662 | |||
663 | static HRESULT AddGroupToList( | ||
664 | __inout SCA_GROUP** ppsgList | ||
665 | ) | ||
666 | { | ||
667 | HRESULT hr = S_OK; | ||
668 | SCA_GROUP* psg = static_cast<SCA_GROUP*>(MemAlloc(sizeof(SCA_GROUP), TRUE)); | ||
669 | ExitOnNull(psg, hr, E_OUTOFMEMORY, "failed to allocate memory for new group list element"); | ||
670 | |||
671 | psg->psgNext = *ppsgList; | ||
672 | *ppsgList = psg; | ||
673 | |||
674 | LExit: | ||
675 | return hr; | ||
676 | } | ||