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