aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/userutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/userutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/userutil.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/userutil.cpp b/src/libs/dutil/WixToolset.DUtil/userutil.cpp
new file mode 100644
index 00000000..ca6d5480
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/userutil.cpp
@@ -0,0 +1,300 @@
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// UserExit macros
7#define UserExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
8#define UserExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
9#define UserExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
10#define UserExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
11#define UserExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
12#define UserExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
13#define UserExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__)
14#define UserExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__)
15#define UserExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__)
16#define UserExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__)
17#define UserExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_USERUTIL, e, x, s, __VA_ARGS__)
18#define UserExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_USERUTIL, g, x, s, __VA_ARGS__)
19
20static BOOL CheckIsMemberHelper(
21 __in_z LPCWSTR pwzGroupUserDomain,
22 __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData,
23 __in DWORD cguiGroupData
24 );
25
26/*******************************************************************
27 UserBuildDomainUserName - builds a DOMAIN\USERNAME string
28
29********************************************************************/
30extern "C" HRESULT DAPI UserBuildDomainUserName(
31 __out_ecount_z(cchDest) LPWSTR wzDest,
32 __in int cchDest,
33 __in_z LPCWSTR pwzName,
34 __in_z LPCWSTR pwzDomain
35 )
36{
37 HRESULT hr = S_OK;
38 DWORD cchLeft = cchDest;
39 WCHAR* pwz = wzDest;
40 DWORD cchWz = cchDest;
41 DWORD cch;
42
43 cch = lstrlenW(pwzDomain);
44 if (cch >= cchLeft)
45 {
46 hr = ERROR_MORE_DATA;
47 UserExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain);
48 }
49 else if (cch > 0)
50 {
51 // handle the domain case
52
53 hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0'
54 UserExitOnFailure(hr, "Failed to copy Domain onto string.");
55
56 cchLeft -= cch;
57 pwz += cch;
58 cchWz -= cch;
59
60 if (1 >= cchLeft)
61 {
62 hr = ERROR_MORE_DATA;
63 UserExitOnFailure(hr, "Insufficient buffer size while building domain user name");
64 }
65
66 hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0'
67 UserExitOnFailure(hr, "Failed to copy backslash onto string.");
68
69 --cchLeft;
70 ++pwz;
71 --cchWz;
72 }
73
74 cch = lstrlenW(pwzName);
75 if (cch >= cchLeft)
76 {
77 hr = ERROR_MORE_DATA;
78 UserExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName);
79 }
80
81 hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0'
82 UserExitOnFailure(hr, "Failed to copy User name onto string.");
83
84LExit:
85 return hr;
86}
87
88
89/*******************************************************************
90 Checks whether a user is a member of a group - outputs the result via lpfMember
91********************************************************************/
92extern "C" HRESULT DAPI UserCheckIsMember(
93 __in_z LPCWSTR pwzName,
94 __in_z LPCWSTR pwzDomain,
95 __in_z LPCWSTR pwzGroupName,
96 __in_z LPCWSTR pwzGroupDomain,
97 __out LPBOOL lpfMember
98 )
99{
100 HRESULT hr = S_OK;
101 UINT er = ERROR_SUCCESS;
102
103 DWORD dwRead = 0;
104 DWORD dwTotal = 0;
105 LPCWSTR wz = NULL;
106 GROUP_USERS_INFO_0 *pguiGroupData = NULL;
107 WCHAR wzGroupUserDomain[MAX_DARWIN_COLUMN + 1]; // GROUPDOMAIN\GROUPNAME
108 WCHAR wzUserDomain[MAX_DARWIN_COLUMN + 1]; // USERDOMAIN\USERNAME
109 BSTR bstrUser = NULL;
110 BSTR bstrGroup = NULL;
111
112 IADsGroup *pGroup = NULL;
113 VARIANT_BOOL vtBoolResult = VARIANT_FALSE;
114
115 hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain);
116 UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName);
117
118 hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain);
119 UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName);
120
121 if (pwzDomain && *pwzDomain)
122 {
123 wz = pwzDomain;
124 }
125
126 er = ::NetUserGetGroups(wz, pwzName, 0, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal);
127 // Ignore these errors, and just go to the fallback checks
128 if (ERROR_BAD_NETPATH == er || ERROR_INVALID_NAME == er || NERR_UserNotFound == er)
129 {
130 Trace(REPORT_VERBOSE, "failed to get groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er));
131 er = ERROR_SUCCESS;
132 }
133 UserExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName);
134
135 if (dwRead != dwTotal)
136 {
137 hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
138 UserExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName);
139 }
140
141 if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead))
142 {
143 *lpfMember = TRUE;
144 ExitFunction1(hr = S_OK);
145 }
146
147 if (NULL != pguiGroupData)
148 {
149 ::NetApiBufferFree(pguiGroupData);
150 pguiGroupData = NULL;
151 }
152
153 // If we fail with the global groups, try again with the local groups
154 er = ::NetUserGetLocalGroups(NULL, wzUserDomain, 0, LG_INCLUDE_INDIRECT, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal);
155 // Ignore these errors, and just go to the fallback checks
156 if (NERR_UserNotFound == er || NERR_DCNotFound == er || RPC_S_SERVER_UNAVAILABLE == er)
157 {
158 Trace(REPORT_VERBOSE, "failed to get local groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er));
159 er = ERROR_SUCCESS;
160 }
161 UserExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName);
162
163 if (dwRead != dwTotal)
164 {
165 hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
166 UserExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName);
167 }
168
169 if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead))
170 {
171 *lpfMember = TRUE;
172 ExitFunction1(hr = S_OK);
173 }
174
175 // If the above methods failed, let's try active directory
176 hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser);
177 UserExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain);
178
179 hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup);
180 UserExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain);
181
182 if (lstrlenW(pwzGroupDomain) > 0)
183 {
184 hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
185 UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast<WCHAR*>(bstrGroup) );
186
187 hr = pGroup->IsMember(bstrUser, &vtBoolResult);
188 UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
189 }
190
191 if (vtBoolResult)
192 {
193 *lpfMember = TRUE;
194 ExitFunction1(hr = S_OK);
195 }
196
197 hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
198 UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast<WCHAR*>(bstrGroup) );
199
200 hr = pGroup->IsMember(bstrUser, &vtBoolResult);
201 UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
202
203 if (vtBoolResult)
204 {
205 *lpfMember = TRUE;
206 ExitFunction1(hr = S_OK);
207 }
208
209LExit:
210 ReleaseObject(pGroup);
211 ReleaseBSTR(bstrUser);
212 ReleaseBSTR(bstrGroup);
213
214 if (NULL != pguiGroupData)
215 {
216 ::NetApiBufferFree(pguiGroupData);
217 }
218
219 return hr;
220}
221
222
223/*******************************************************************
224 Takes a domain and name, and allocates a BSTR which represents
225 DOMAIN\NAME's active directory path. The BSTR this function returns
226 should be released manually using the ReleaseBSTR() macro.
227********************************************************************/
228extern "C" HRESULT DAPI UserCreateADsPath(
229 __in_z LPCWSTR wzObjectDomain,
230 __in_z LPCWSTR wzObjectName,
231 __out BSTR *pbstrAdsPath
232 )
233{
234 Assert(wzObjectDomain && wzObjectName && *wzObjectName);
235
236 HRESULT hr = S_OK;
237 LPWSTR pwzAdsPath = NULL;
238
239 hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0);
240 UserExitOnFailure(hr, "failed to allocate AdsPath string");
241
242 if (*wzObjectDomain)
243 {
244 hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName);
245 UserExitOnFailure(hr, "failed to allocate AdsPath string");
246 }
247 else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/"))
248 {
249 hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0);
250 UserExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName);
251 }
252 else
253 {
254 hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0);
255 UserExitOnFailure(hr, "failed to concat LocalHost/");
256
257 hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0);
258 UserExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName);
259 }
260
261 *pbstrAdsPath = ::SysAllocString(pwzAdsPath);
262 if (NULL == *pbstrAdsPath)
263 {
264 hr = E_OUTOFMEMORY;
265 }
266
267LExit:
268 ReleaseStr(pwzAdsPath);
269
270 return hr;
271}
272
273
274/*******************************************************************
275 Helper function to check if pwzGroupUserDomain (in form of "domain\username" is
276 a member of a given LOCALGROUP_USERS_INFO_0 structure. Useful to pass in the
277 output from both NetUserGetGroups() and NetUserGetLocalGroups()
278********************************************************************/
279static BOOL CheckIsMemberHelper(
280 __in_z LPCWSTR pwzGroupUserDomain,
281 __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData,
282 __in DWORD cguiGroupData
283 )
284{
285 if (NULL == pguiGroupData)
286 {
287 return FALSE;
288 }
289
290 for (DWORD dwCounter = 0; dwCounter < cguiGroupData; ++dwCounter)
291 {
292 // If the user is a member of the group, set the output flag to true
293 if (0 == lstrcmpiW(pwzGroupUserDomain, pguiGroupData[dwCounter].grui0_name))
294 {
295 return TRUE;
296 }
297 }
298
299 return FALSE;
300}