DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
UserPrincipal.cpp
Go to the documentation of this file.
1#include "pch.h"
5
6#include <vector>
7#include <string>
8
9#if defined(_WIN32)
10#include <windows.h>
11#include <lm.h>
12#include <sddl.h>
13#include "Win32Internal.h"
15#pragma comment(lib, "netapi32.lib")
16#pragma comment(lib, "advapi32.lib")
17using namespace DotNetDupe::System::Internal;
18#else
19#include <pwd.h>
20#include <grp.h>
21#include <sys/types.h>
22#include <unistd.h>
23#include <cerrno>
25#endif
26
27namespace DotNetDupe {
28 namespace System {
29 namespace Security {
30 namespace Principal {
31
35
39
40#if defined(_WIN32)
41 static UserClass ClassifyWindowsUser(DWORD dwPriv, DWORD dwFlags) {
43 if (dwFlags & UF_ACCOUNTDISABLE) return UserClass::Guest;
44 if (dwPriv == USER_PRIV_ADMIN) return UserClass::Admin;
45 if (dwPriv == USER_PRIV_GUEST) return UserClass::Guest;
46 return UserClass::Normal;
47 }
48
49 static void PopulateWin32UserGroups(LPCWSTR pwszUser, Collections::Generic::List<String>& lstGroups, Collections::Generic::List<String>& lstPermissions) {
51 LPGROUP_USERS_INFO_0 pGroups = NULL;
52 DWORD dwEntriesRead = 0, dwTotalEntries = 0;
53
54 NET_API_STATUS nStatus = ::NetUserGetGroups(NULL, pwszUser, 0, (LPBYTE*)&pGroups, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries);
55 if (nStatus == ERROR_ACCESS_DENIED) {
56 throw UnauthorizedAccessException("Access denied querying user groups. Administrator privileges required.");
57 }
58
60 if (nStatus == NERR_Success) {
61 for (DWORD i = 0; i < dwEntriesRead; i++) {
62 std::string sGroup = StringConvertInternal::WCharToUtf8(pGroups[i].grui0_name);
63 lstPermissions.Add(String("GroupMember:") + sGroup.c_str());
64 lstGroups.Add(sGroup.c_str());
65 }
66 ::NetApiBufferFree(pGroups);
67 }
68 }
69
70 static void PopulateWin32SidAndDomain(LPCWSTR pwszUser, UserInfo& info) {
72 BYTE sidBuffer[SECURITY_MAX_SID_SIZE];
73 DWORD cbSid = sizeof(sidBuffer);
74 WCHAR szDomain[256] = { 0 };
75 DWORD cchDomain = 256;
76 SID_NAME_USE peUse;
77
78 if (::LookupAccountNameW(NULL, pwszUser, (PSID)sidBuffer, &cbSid, szDomain, &cchDomain, &peUse)) {
79 LPWSTR pszStringSid = NULL;
80 if (::ConvertSidToStringSidW((PSID)sidBuffer, &pszStringSid)) {
81 info.sSidOrUid = StringConvertInternal::WCharToUtf8(pszStringSid).c_str();
82 ::LocalFree(pszStringSid);
83 }
84 if (cchDomain > 0) {
85 info.sDomain = StringConvertInternal::WCharToUtf8(szDomain).c_str();
86 }
87 }
88 }
89
90 static UserInfo BuildWin32UserInfo(const USER_INFO_1* pUi) {
92 UserInfo info;
93 std::string sName = StringConvertInternal::WCharToUtf8(pUi->usri1_name);
94 info.sUsername = sName.c_str();
95
96 info.sDomain = "LOCAL";
97 info.sSidOrUid = "S-1-5-21-USER";
98 info.eUserClass = ClassifyWindowsUser(pUi->usri1_priv, pUi->usri1_flags);
99 info.bIsDisabled = (pUi->usri1_flags & UF_ACCOUNTDISABLE) != 0;
100 info.bIsPasswordRequired = (pUi->usri1_flags & UF_PASSWD_NOTREQD) == 0;
101 info.bIsAccountLocked = (pUi->usri1_flags & UF_LOCKOUT) != 0;
102
103 if (info.eUserClass == UserClass::Admin) {
104 info.lstPermissions.Add("AdministratorRights");
105 info.lstPermissions.Add("FullControl");
106 } else {
107 info.lstPermissions.Add("StandardUserRights");
108 }
109
110 PopulateWin32SidAndDomain(pUi->usri1_name, info);
111 PopulateWin32UserGroups(pUi->usri1_name, info.lstGroups, info.lstPermissions);
112 return info;
113 }
114
117 LPUSER_INFO_1 pBuf = NULL;
118 DWORD dwEntriesRead = 0, dwTotalEntries = 0, dwResumeHandle = 0;
119
120 NET_API_STATUS nStatus = ::NetUserEnum(NULL, 1, FILTER_NORMAL_ACCOUNT, (LPBYTE*)&pBuf, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries, &dwResumeHandle);
121 if (nStatus == ERROR_ACCESS_DENIED) {
122 throw UnauthorizedAccessException("Access denied enumerating user accounts. Administrator privileges required.");
123 }
124
125 if (nStatus == NERR_Success || nStatus == ERROR_MORE_DATA) {
126 for (DWORD i = 0; i < dwEntriesRead; i++) {
127 lstUsers.Add(BuildWin32UserInfo(&pBuf[i]));
128 }
129 }
130
131 if (pBuf != NULL) {
132 ::NetApiBufferFree(pBuf);
133 }
134 }
135#else
136 static UserInfo BuildLinuxUserInfo(const struct passwd* pw) {
138 UserInfo info;
139 info.sUsername = pw->pw_name;
140 info.sDomain = "LOCAL";
141 info.sSidOrUid = std::to_string(pw->pw_uid).c_str();
142 info.bIsDisabled = false;
143 info.bIsPasswordRequired = true;
144 info.bIsAccountLocked = false;
145
146 if (pw->pw_uid == 0) {
147 info.eUserClass = UserClass::Admin;
148 info.lstPermissions.Add("RootPrivileges");
149 info.lstPermissions.Add("FullControl");
150 } else if (pw->pw_uid < 1000) {
151 info.eUserClass = UserClass::System;
152 info.lstPermissions.Add("SystemDaemonRights");
153 } else {
154 info.eUserClass = UserClass::Normal;
155 info.lstPermissions.Add("StandardUserRights");
156 }
157
158 return info;
159 }
160
161 static void EnumerateLinuxUsers(Collections::Generic::List<UserInfo>& lstUsers) {
163 errno = 0;
164 setpwent();
165 struct passwd* pw;
166 while ((pw = getpwent()) != NULL) {
167 lstUsers.Add(BuildLinuxUserInfo(pw));
168 }
169 if (errno == EACCES || errno == EPERM) {
170 endpwent();
171 throw UnauthorizedAccessException("Access denied enumerating user accounts. Root privileges required.");
172 }
173 endpwent();
174 }
175#endif
176
180
181#if defined(_WIN32)
182 EnumerateWin32Users(lstUsers);
183#else
184 EnumerateLinuxUsers(lstUsers);
185#endif
186
187 return lstUsers;
188 }
189
190#if defined(_WIN32)
191 static UserInfo QueryWin32User(const String& sUsername) {
193 std::wstring wUsername = StringConvertInternal::Utf8ToWChar(sUsername.GetRawString() ? sUsername.GetRawString() : "");
194 LPUSER_INFO_1 pBuf = NULL;
195 NET_API_STATUS nStatus = ::NetUserGetInfo(NULL, wUsername.c_str(), 1, (LPBYTE*)&pBuf);
196 if (nStatus == NERR_Success && pBuf != NULL) {
197 UserInfo info = BuildWin32UserInfo(pBuf);
198 ::NetApiBufferFree(pBuf);
199 return info;
200 }
201 if (nStatus == ERROR_ACCESS_DENIED) throw UnauthorizedAccessException("Access denied querying user information for: " + sUsername);
202 if (nStatus == NERR_UserNotFound || nStatus == ERROR_NO_SUCH_USER) throw ArgumentException("User not found: " + sUsername);
203 throw ComponentModel::Win32Exception(nStatus, "Failed to query user information for: " + sUsername);
204 }
205
206 static std::string GetCurrentWin32UserName() {
208 WCHAR szName[256] = { 0 };
209 DWORD dwSize = 256;
210 if (!::GetUserNameW(szName, &dwSize)) {
211 DWORD dwErr = ::GetLastError();
212 if (dwErr == ERROR_ACCESS_DENIED) throw UnauthorizedAccessException("Access denied querying current user name.");
213 throw ComponentModel::Win32Exception(dwErr, "Failed to get current user name.");
214 }
216 }
217#else
218 static UserInfo QueryLinuxUser(const String& sUsername) {
220 errno = 0;
221 struct passwd* pw = getpwnam(sUsername.GetRawString());
222 if (pw == NULL) {
223 if (errno == EACCES || errno == EPERM) throw UnauthorizedAccessException("Access denied querying user information for: " + sUsername);
224 throw ArgumentException("User not found: " + sUsername);
225 }
226 return BuildLinuxUserInfo(pw);
227 }
228
229 static UserInfo QueryCurrentLinuxUser() {
231 errno = 0;
232 struct passwd* pw = getpwuid(getuid());
233 if (pw == NULL) {
234 if (errno == EACCES || errno == EPERM) throw UnauthorizedAccessException("Access denied querying current user information.");
235 throw SystemException("Failed to query current user information.");
236 }
237 return BuildLinuxUserInfo(pw);
238 }
239#endif
240
243 if (sUsername.IsEmpty()) throw ArgumentException("Username cannot be empty.");
244#if defined(_WIN32)
245 return QueryWin32User(sUsername);
246#else
247 return QueryLinuxUser(sUsername);
248#endif
249 }
250
253#if defined(_WIN32)
254 return GetUser(GetCurrentWin32UserName().c_str());
255#else
256 return QueryCurrentLinuxUser();
257#endif
258 }
259
260 }
261 }
262 }
263}
Defines the exception thrown when an invalid argument is provided to a method.
Serves as the base class for system exceptions across the library.
The exception that is thrown when the operating system denies access because of an I/O error or a spe...
User principal and account enumeration operations per POSIX and Win32 security APIs.
Exception thrown for a Win32 or platform-native error code.
ArgumentException(const String &sMessage)
Initializes a new instance of the ArgumentException class with a specified error message.
Represents a strongly typed list of objects accessible by index.
Definition List.h:29
void Add(const T &item)
Adds an object to the end of the List.
Definition List.h:138
Exception thrown for a Win32 or POSIX platform error code.
static std::wstring Utf8ToWChar(const char *pUtf8Str)
static std::string WCharToUtf8(const wchar_t *pWStr)
static Collections::Generic::List< UserInfo > EnumerateUsers()
Enumerates all registered local user accounts on the host system.
static UserInfo GetCurrent()
Retrieves account information for the currently executing process user.
static UserInfo GetUser(const String &sUsername)
Retrieves account information for a specified username.
UserPrincipal()
Initializes a new instance of the UserPrincipal class.
virtual ~UserPrincipal()
Releases resources used by the UserPrincipal.
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
Definition String.h:74
String()
Initializes a new instance of the String class to an empty string.
Definition String.cpp:64
const char * GetRawString() const
Definition String.cpp:230
SystemException()
Initializes a new instance of the SystemException class with a default message.
Definition Exception.cpp:48
UnauthorizedAccessException()
Initializes a new instance of the UnauthorizedAccessException class with a default message.
Definition Exception.cpp:52
static void PopulateWin32SidAndDomain(LPCWSTR pwszUser, UserInfo &info)
static void PopulateWin32UserGroups(LPCWSTR pwszUser, Collections::Generic::List< String > &lstGroups, Collections::Generic::List< String > &lstPermissions)
static UserInfo BuildWin32UserInfo(const USER_INFO_1 *pUi)
static UserClass ClassifyWindowsUser(DWORD dwPriv, DWORD dwFlags)
UserClass
Defines user account classification categories.
@ Normal
Standard authenticated user account.
@ System
System service or daemon account.
@ Admin
Administrative or root user account.
static void EnumerateWin32Users(Collections::Generic::List< UserInfo > &lstUsers)
static UserInfo QueryWin32User(const String &sUsername)
@ UserInfo
User name and password authorization data.
Definition UriEnums.h:18
Represents platform user account information and privileges.
bool bIsPasswordRequired
Indicates whether a password is required.
bool bIsDisabled
Indicates whether the account is disabled.
UserClass eUserClass
Classification level of the user.
String sSidOrUid
Security identifier (SID on Windows, UID on POSIX).
String sDomain
Domain or host machine name.
String sUsername
User login or account name.
Collections::Generic::List< String > lstPermissions
List of assigned permission strings.
Collections::Generic::List< String > lstGroups
List of security groups the user belongs to.
bool bIsAccountLocked
Indicates whether the account is currently locked out.