aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Util/ca/scagroup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Util/ca/scagroup.cpp')
-rw-r--r--src/ext/Util/ca/scagroup.cpp503
1 files changed, 503 insertions, 0 deletions
diff --git a/src/ext/Util/ca/scagroup.cpp b/src/ext/Util/ca/scagroup.cpp
new file mode 100644
index 00000000..c484c1d2
--- /dev/null
+++ b/src/ext/Util/ca/scagroup.cpp
@@ -0,0 +1,503 @@
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#include "scanet.h"
5
6LPCWSTR vcsGroupQuery = L"SELECT `Group`, `Component_`, `Name`, `Domain` FROM `Wix4Group` WHERE `Group`=?";
7enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain };
8
9LPCWSTR vcsGroupGroupQuery = L"SELECT `Parent_`, `Child_` FROM `Wix6GroupGroup` WHERE `Child_`=?";
10enum eGroupGroupQuery { vggqParent = 1, vggqChild };
11
12LPCWSTR vActionableGroupQuery = L"SELECT `Group`,`Component_`,`Name`,`Domain`,`Comment`,`Attributes` FROM `Wix4Group`,`Wix6Group` WHERE `Component_` IS NOT NULL AND `Group`=`Group_`";
13enum eActionableGroupQuery { vagqGroup = 1, vagqComponent, vagqName, vagqDomain, vagqComment, vagqAttributes };
14
15static HRESULT AddGroupToList(
16 __inout SCA_GROUP** ppsgList
17 );
18
19
20HRESULT __stdcall ScaGetGroup(
21 __in LPCWSTR wzGroup,
22 __out SCA_GROUP* pscag
23 )
24{
25 if (!wzGroup || *wzGroup==0 || !pscag)
26 {
27 return E_INVALIDARG;
28 }
29
30 HRESULT hr = S_OK;
31 PMSIHANDLE hView, hRec;
32
33 LPWSTR pwzData = NULL;
34
35 hRec = ::MsiCreateRecord(1);
36 hr = WcaSetRecordString(hRec, 1, wzGroup);
37 ExitOnFailure(hr, "Failed to look up Group");
38
39 hr = WcaOpenView(vcsGroupQuery, &hView);
40 ExitOnFailure(hr, "Failed to open view on Wix4Group table");
41 hr = WcaExecuteView(hView, hRec);
42 ExitOnFailure(hr, "Failed to execute view on Wix4Group table");
43
44 hr = WcaFetchSingleRecord(hView, &hRec);
45 if (S_OK == hr)
46 {
47 hr = WcaGetRecordString(hRec, vgqGroup, &pwzData);
48 ExitOnFailure(hr, "Failed to get Wix4Group.Group");
49 hr = ::StringCchCopyW(pscag->wzKey, countof(pscag->wzKey), pwzData);
50 ExitOnFailure(hr, "Failed to copy key string to group object");
51
52 hr = WcaGetRecordString(hRec, vgqComponent, &pwzData);
53 ExitOnFailure(hr, "Failed to get Wix4Group.Component_");
54 hr = ::StringCchCopyW(pscag->wzComponent, countof(pscag->wzComponent), pwzData);
55 ExitOnFailure(hr, "Failed to copy component string to group object");
56
57 hr = WcaGetRecordFormattedString(hRec, vgqName, &pwzData);
58 ExitOnFailure(hr, "Failed to get Wix4Group.Name");
59 hr = ::StringCchCopyW(pscag->wzName, countof(pscag->wzName), pwzData);
60 ExitOnFailure(hr, "Failed to copy name string to group object");
61
62 hr = WcaGetRecordFormattedString(hRec, vgqDomain, &pwzData);
63 ExitOnFailure(hr, "Failed to get Wix4Group.Domain");
64 hr = ::StringCchCopyW(pscag->wzDomain, countof(pscag->wzDomain), pwzData);
65 ExitOnFailure(hr, "Failed to copy domain string to group object");
66 }
67 else if (E_NOMOREITEMS == hr)
68 {
69 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4Group.Group='%ls'", wzGroup);
70 hr = E_FAIL;
71 }
72 else
73 {
74 ExitOnFailure(hr, "Error or found multiple matching Wix4Group rows");
75 }
76
77LExit:
78 ReleaseStr(pwzData);
79
80 return hr;
81}
82
83HRESULT __stdcall ScaGetGroupDeferred(
84 __in LPCWSTR wzGroup,
85 __in WCA_WRAPQUERY_HANDLE hGroupQuery,
86 __out SCA_USER* pscag
87 )
88{
89 if (!wzGroup || !pscag)
90 {
91 return E_INVALIDARG;
92 }
93
94 HRESULT hr = S_OK;
95 MSIHANDLE hRec, hRecTest;
96
97 LPWSTR pwzData = NULL;
98
99 // clear struct and bail right away if no group key was passed to search for
100 ::ZeroMemory(pscag, sizeof(*pscag));
101 if (!*wzGroup)
102 {
103 ExitFunction1(hr = S_OK);
104 }
105
106 // Reset back to the first record
107 WcaFetchWrappedReset(hGroupQuery);
108
109 hr = WcaFetchWrappedRecordWhereString(hGroupQuery, vgqGroup, wzGroup, &hRec);
110 if (S_OK == hr)
111 {
112 hr = WcaFetchWrappedRecordWhereString(hGroupQuery, vgqGroup, wzGroup, &hRecTest);
113 if (S_OK == hr)
114 {
115 AssertSz(FALSE, "Found multiple matching Wix4Group rows");
116 }
117
118 hr = WcaGetRecordString(hRec, vgqGroup, &pwzData);
119 ExitOnFailure(hr, "Failed to get Wix4Group.Group");
120 hr = ::StringCchCopyW(pscag->wzKey, countof(pscag->wzKey), pwzData);
121 ExitOnFailure(hr, "Failed to copy key string to group object (in deferred CA)");
122
123 hr = WcaGetRecordString(hRec, vgqComponent, &pwzData);
124 ExitOnFailure(hr, "Failed to get Wix4Group.Component_");
125 hr = ::StringCchCopyW(pscag->wzComponent, countof(pscag->wzComponent), pwzData);
126 ExitOnFailure(hr, "Failed to copy component string to group object (in deferred CA)");
127
128 hr = WcaGetRecordString(hRec, vgqName, &pwzData);
129 ExitOnFailure(hr, "Failed to get Wix4Group.Name");
130 hr = ::StringCchCopyW(pscag->wzName, countof(pscag->wzName), pwzData);
131 ExitOnFailure(hr, "Failed to copy name string to group object (in deferred CA)");
132
133 hr = WcaGetRecordString(hRec, vgqDomain, &pwzData);
134 ExitOnFailure(hr, "Failed to get Wix4Group.Domain");
135 hr = ::StringCchCopyW(pscag->wzDomain, countof(pscag->wzDomain), pwzData);
136 ExitOnFailure(hr, "Failed to copy domain string to group object (in deferred CA)");
137 }
138 else if (E_NOMOREITEMS == hr)
139 {
140 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4Group.Group='%ls'", wzGroup);
141 hr = E_FAIL;
142 }
143 else
144 {
145 ExitOnFailure(hr, "Error fetching single Wix4Group row");
146 }
147
148LExit:
149 ReleaseStr(pwzData);
150
151 return hr;
152}
153
154void ScaGroupFreeList(
155 __in SCA_GROUP* psgList
156 )
157{
158 SCA_GROUP* psgDelete = psgList;
159 while (psgList)
160 {
161 psgDelete = psgList;
162 psgList = psgList->psgNext;
163
164 MemFree(psgDelete);
165 }
166}
167
168
169HRESULT ScaGroupRead(
170 __out SCA_GROUP** ppsgList
171 )
172{
173 //Assert(FALSE);
174 Assert(ppsgList);
175
176 HRESULT hr = S_OK;
177 UINT er = ERROR_SUCCESS;
178 PMSIHANDLE hView, hRec, hGroupRec, hGroupGroupView;
179
180 LPWSTR pwzData = NULL;
181
182 BOOL fGroupGroupExists = FALSE;
183
184 SCA_GROUP *psg = NULL;
185
186 INSTALLSTATE isInstalled, isAction;
187
188 if (S_OK != WcaTableExists(L"Wix4Group"))
189 {
190 WcaLog(LOGMSG_VERBOSE, "Wix4Group Table does not exist, exiting");
191 ExitFunction1(hr = S_FALSE);
192 }
193 if (S_OK != WcaTableExists(L"Wix6Group"))
194 {
195 WcaLog(LOGMSG_VERBOSE, "Wix6Group Table does not exist, exiting");
196 ExitFunction1(hr = S_FALSE);
197 }
198
199 if (S_OK == WcaTableExists(L"Wix6GroupGroup"))
200 {
201 fGroupGroupExists = TRUE;
202 }
203
204 //
205 // loop through all the groups
206 //
207 hr = WcaOpenExecuteView(vActionableGroupQuery, &hView);
208 ExitOnFailure(hr, "failed to open view on Wix4Group,Wix6Group table(s)");
209 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
210 {
211 hr = WcaGetRecordString(hRec, vagqComponent, &pwzData);
212 ExitOnFailure(hr, "failed to get Wix4Group.Component");
213
214 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction);
215 hr = HRESULT_FROM_WIN32(er);
216 ExitOnFailure(hr, "failed to get Component state for Wix4Group");
217
218 // don't bother if we aren't installing or uninstalling this component
219 if (WcaIsInstalling(isInstalled, isAction) || WcaIsUninstalling(isInstalled, isAction))
220 {
221 //
222 // Add the group to the list and populate it's values
223 //
224 hr = AddGroupToList(ppsgList);
225 ExitOnFailure(hr, "failed to add group to list");
226
227 psg = *ppsgList;
228
229 psg->isInstalled = isInstalled;
230 psg->isAction = isAction;
231 hr = ::StringCchCopyW(psg->wzComponent, countof(psg->wzComponent), pwzData);
232 ExitOnFailure(hr, "failed to copy component name: %ls", pwzData);
233
234 hr = WcaGetRecordString(hRec, vagqGroup, &pwzData);
235 ExitOnFailure(hr, "failed to get Wix4Group.Group");
236 hr = ::StringCchCopyW(psg->wzKey, countof(psg->wzKey), pwzData);
237 ExitOnFailure(hr, "failed to copy group key: %ls", pwzData);
238
239 hr = WcaGetRecordFormattedString(hRec, vagqName, &pwzData);
240 ExitOnFailure(hr, "failed to get Wix4Group.Name");
241 hr = ::StringCchCopyW(psg->wzName, countof(psg->wzName), pwzData);
242 ExitOnFailure(hr, "failed to copy group name: %ls", pwzData);
243
244 hr = WcaGetRecordFormattedString(hRec, vagqDomain, &pwzData);
245 ExitOnFailure(hr, "failed to get Wix4Group.Domain");
246 hr = ::StringCchCopyW(psg->wzDomain, countof(psg->wzDomain), pwzData);
247 ExitOnFailure(hr, "failed to copy group domain: %ls", pwzData);
248 hr = WcaGetRecordFormattedString(hRec, vagqComment, &pwzData);
249 ExitOnFailure(hr, "failed to get Wix6Group.Comment");
250 hr = ::StringCchCopyW(psg->wzComment, countof(psg->wzComment), pwzData);
251 ExitOnFailure(hr, "failed to copy group comment: %ls", pwzData);
252
253 hr = WcaGetRecordInteger(hRec, vagqAttributes, &psg->iAttributes);
254 ExitOnFailure(hr, "failed to get Wix6Group.Attributes");
255
256 // Check if this group is to be added to any other groups
257 if (fGroupGroupExists)
258 {
259 hGroupRec = ::MsiCreateRecord(1);
260 hr = WcaSetRecordString(hGroupRec, 1, psg->wzKey);
261 ExitOnFailure(hr, "Failed to create group record for querying Wix6GroupGroup table");
262
263 hr = WcaOpenExecuteView(vcsGroupGroupQuery, &hGroupGroupView);
264 ExitOnFailure(hr, "Failed to open view on Wix6GroupGroup table for group %ls", psg->wzKey);/*
265 hr = WcaExecuteView(hGroupGroupView, hGroupRec);
266 ExitOnFailure(hr, "Failed to execute view on Wix6GroupGroup table for group: %ls", psg->wzKey);*/
267
268 while (S_OK == (hr = WcaFetchRecord(hGroupGroupView, &hRec)))
269 {
270 hr = WcaGetRecordString(hRec, vggqParent, &pwzData);
271 ExitOnFailure(hr, "failed to get Wix6GroupGroup.Parent");
272
273 hr = AddGroupToList(&(psg->psgGroups));
274 ExitOnFailure(hr, "failed to add group to list");
275
276 hr = ScaGetGroup(pwzData, psg->psgGroups);
277 ExitOnFailure(hr, "failed to get information for group: %ls", pwzData);
278 }
279
280 if (E_NOMOREITEMS == hr)
281 {
282 hr = S_OK;
283 }
284 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4UserGroup table");
285 }
286 }
287 }
288
289 if (E_NOMOREITEMS == hr)
290 {
291 hr = S_OK;
292 }
293 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4Group table");
294
295LExit:
296 ReleaseStr(pwzData);
297
298 return hr;
299}
300
301/* ****************************************************************
302ScaGroupExecute - Schedules group account creation or removal based on
303component state.
304
305******************************************************************/
306HRESULT ScaGroupExecute(
307 __in SCA_GROUP *psgList
308 )
309{
310 HRESULT hr = S_OK;
311 DWORD er = 0;
312
313 LPWSTR pwzBaseScriptKey = NULL;
314 DWORD cScriptKey = 0;
315
316 LOCALGROUP_INFO_0 *pGroupInfo = NULL;
317 LPWSTR pwzScriptKey = NULL;
318 LPWSTR pwzActionData = NULL;
319 LPWSTR pwzRollbackData = NULL;
320 LPWSTR pwzServerName = NULL;
321
322 // Get the base script key for this CustomAction.
323 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
324 ExitOnFailure(hr, "Failed to get encoding key.");
325
326 // Loop through all the users to be configured.
327 for (SCA_GROUP *psg = psgList; psg; psg = psg->psgNext)
328 {
329 GROUP_EXISTS geGroupExists = GROUP_EXISTS_INDETERMINATE;
330
331 // Always put the Group Name, Domain, and Comment on the front of the CustomAction data.
332 // The attributes will be added when we have finished adjusting them. Sometimes we'll
333 // add more data.
334 Assert(psg->wzName);
335 hr = WcaWriteStringToCaData(psg->wzName, &pwzActionData);
336 ExitOnFailure(hr, "Failed to add group name to custom action data: %ls", psg->wzName);
337 hr = WcaWriteStringToCaData(psg->wzDomain, &pwzActionData);
338 ExitOnFailure(hr, "Failed to add group domain to custom action data: %ls", psg->wzDomain);
339 hr = WcaWriteStringToCaData(psg->wzComment, &pwzActionData);
340 ExitOnFailure(hr, "Failed to add group comment to custom action data: %ls", psg->wzComment);
341
342 // Check to see if the group already exists since we have to be very careful when adding
343 // and removing groups. Note: MSDN says that it is safe to call these APIs from any
344 // user, so we should be safe calling it during immediate mode.
345
346 LPCWSTR wzDomain = psg->wzDomain;
347 hr = GetDomainServerName(wzDomain, &pwzServerName);
348
349 er = ::NetLocalGroupGetInfo(pwzServerName, psg->wzName, 0, reinterpret_cast<LPBYTE*>(&pGroupInfo));
350 if (NERR_Success == er)
351 {
352 geGroupExists = GROUP_EXISTS_YES;
353 }
354 else if (NERR_GroupNotFound == er)
355 {
356 geGroupExists = GROUP_EXISTS_NO;
357 }
358 else
359 {
360 geGroupExists = GROUP_EXISTS_INDETERMINATE;
361 hr = HRESULT_FROM_WIN32(er);
362 WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, group: %ls (error code 0x%x) - continuing", wzDomain, psg->wzName, hr);
363 hr = S_OK;
364 er = ERROR_SUCCESS;
365 }
366
367 if (WcaIsInstalling(psg->isInstalled, psg->isAction))
368 {
369 // If the group exists, check to see if we are supposed to fail if the group exists before
370 // the install.
371 if (GROUP_EXISTS_YES == geGroupExists)
372 {
373 // Re-installs will always fail if we don't remove the check for "fail if exists".
374 if (WcaIsReInstalling(psg->isInstalled, psg->isAction))
375 {
376 psg->iAttributes &= ~SCAG_FAIL_IF_EXISTS;
377
378 // If install would create the group, re-install should be able to update the group.
379 if (!(psg->iAttributes & SCAG_DONT_CREATE_GROUP))
380 {
381 psg->iAttributes |= SCAG_UPDATE_IF_EXISTS;
382 }
383 }
384
385 if (SCAG_FAIL_IF_EXISTS & psg->iAttributes
386 && !(SCAG_UPDATE_IF_EXISTS & psg->iAttributes))
387 {
388 hr = HRESULT_FROM_WIN32(NERR_GroupExists);
389 MessageExitOnFailure(hr, msierrGRPFailedGroupCreateExists, "Failed to create group: %ls because group already exists.", psg->wzName);
390 }
391 }
392
393 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData);
394 ExitOnFailure(hr, "failed to add group attributes to custom action data for group: %ls", psg->wzKey);
395
396 // Rollback only if the group already exists, we couldn't determine if the group exists, or we are going to create the group
397 if ((GROUP_EXISTS_YES == geGroupExists)
398 || (GROUP_EXISTS_INDETERMINATE == geGroupExists)
399 || !(psg->iAttributes & SCAG_DONT_CREATE_GROUP))
400 {
401 ++cScriptKey;
402 hr = StrAllocFormatted(&pwzScriptKey, L"%ls%u", pwzBaseScriptKey, cScriptKey);
403 ExitOnFailure(hr, "Failed to create encoding key.");
404
405 // Write the script key to CustomActionData for install and rollback so information can be passed to rollback.
406 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
407 ExitOnFailure(hr, "Failed to add encoding key to custom action data.");
408
409 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzRollbackData);
410 ExitOnFailure(hr, "Failed to add encoding key to rollback custom action data.");
411
412 INT iRollbackUserAttributes = psg->iAttributes;
413
414 // If the user already exists, ensure this is accounted for in rollback
415 if (GROUP_EXISTS_YES == geGroupExists)
416 {
417 iRollbackUserAttributes |= SCAG_DONT_CREATE_GROUP;
418 }
419 else
420 {
421 iRollbackUserAttributes &= ~SCAG_DONT_CREATE_GROUP;
422 }
423
424 hr = WcaWriteStringToCaData(psg->wzName, &pwzRollbackData);
425 ExitOnFailure(hr, "Failed to add group name to rollback custom action data: %ls", psg->wzName);
426 hr = WcaWriteStringToCaData(psg->wzDomain, &pwzRollbackData);
427 ExitOnFailure(hr, "Failed to add group domain to rollback custom action data: %ls", psg->wzDomain);
428 hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData);
429 ExitOnFailure(hr, "failed to add group attributes to rollback custom action data for group: %ls", psg->wzKey);
430
431 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateGroupRollback"), pwzRollbackData, COST_GROUP_DELETE);
432 ExitOnFailure(hr, "failed to schedule CreateGroupRollback");
433 }
434 else
435 {
436 // Write empty script key to CustomActionData since there is no rollback.
437 hr = WcaWriteStringToCaData(L"", &pwzActionData);
438 ExitOnFailure(hr, "Failed to add empty encoding key to custom action data.");
439 }
440
441 //
442 // Schedule the creation now.
443 //
444 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateGroup"), pwzActionData, COST_GROUP_ADD);
445 ExitOnFailure(hr, "failed to schedule CreateGroup");
446 }
447 else if (((GROUP_EXISTS_YES == geGroupExists)
448 || (GROUP_EXISTS_INDETERMINATE == geGroupExists))
449 && WcaIsUninstalling(psg->isInstalled, psg->isAction)
450 && !(psg->iAttributes & SCAG_DONT_REMOVE_ON_UNINSTALL))
451 {
452 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData);
453 ExitOnFailure(hr, "failed to add group attributes to custom action data for group: %ls", psg->wzKey);
454
455 // Schedule the removal because the group exists and we don't have any flags set
456 // that say not to remove the group on uninstall.
457 //
458 // Note: We can't rollback the removal of a group which is why RemoveGroup is a commit
459 // CustomAction.
460 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RemoveGroup"), pwzActionData, COST_GROUP_DELETE);
461 ExitOnFailure(hr, "failed to schedule RemoveGroup");
462 }
463
464 ReleaseNullStr(pwzScriptKey);
465 ReleaseNullStr(pwzActionData);
466 ReleaseNullStr(pwzRollbackData);
467 ReleaseNullStr(pwzServerName);
468 if (pGroupInfo)
469 {
470 ::NetApiBufferFree(static_cast<LPVOID>(pGroupInfo));
471 pGroupInfo = NULL;
472 }
473 }
474
475LExit:
476 ReleaseStr(pwzBaseScriptKey);
477 ReleaseStr(pwzScriptKey);
478 ReleaseStr(pwzActionData);
479 ReleaseStr(pwzRollbackData);
480 ReleaseStr(pwzServerName);
481 if (pGroupInfo)
482 {
483 ::NetApiBufferFree(static_cast<LPVOID>(pGroupInfo));
484 pGroupInfo = NULL;
485 }
486
487 return hr;
488}
489
490static HRESULT AddGroupToList(
491 __inout SCA_GROUP** ppsgList
492 )
493{
494 HRESULT hr = S_OK;
495 SCA_GROUP* psg = static_cast<SCA_GROUP*>(MemAlloc(sizeof(SCA_GROUP), TRUE));
496 ExitOnNull(psg, hr, E_OUTOFMEMORY, "failed to allocate memory for new group list element");
497
498 psg->psgNext = *ppsgList;
499 *ppsgList = psg;
500
501LExit:
502 return hr;
503}