File indexing completed on 2024-04-28 03:53:51

0001 /*
0002     KUser - represent a user/account (Windows)
0003 
0004     SPDX-FileCopyrightText: 2007 Bernhard Loos <nhuh.put@web.de>
0005     SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kuser.h"
0011 
0012 #include "kcoreaddons_debug.h"
0013 #include <QDir>
0014 #include <QStandardPaths>
0015 
0016 #include <memory> // unique_ptr
0017 #include <type_traits>
0018 
0019 #include <qt_windows.h> // Must be included before lm.h
0020 
0021 #include <lm.h> //Net*
0022 
0023 #include <sddl.h> //ConvertSidToStringSidW
0024 #include <shlobj.h>
0025 #include <userenv.h> //GetProfilesDirectoryW
0026 
0027 // this can't be a lambda due to a MSVC2012 bug
0028 // (works fine in 2010 and 2013)
0029 struct netApiBufferDeleter {
0030     void operator()(void *buffer)
0031     {
0032         if (buffer) {
0033             NetApiBufferFree(buffer);
0034         }
0035     }
0036 };
0037 
0038 template<typename T>
0039 class ScopedNetApiBuffer : public std::unique_ptr<T, netApiBufferDeleter>
0040 {
0041 public:
0042     // explicit scope resolution operator needed in ::netApiBufferDeleter
0043     // because of *another* MSVC bug :(
0044     inline explicit ScopedNetApiBuffer(T *data)
0045         : std::unique_ptr<T, ::netApiBufferDeleter>(data, ::netApiBufferDeleter())
0046     {
0047     }
0048 };
0049 
0050 const auto handleCloser = [](HANDLE h) {
0051     if (h != INVALID_HANDLE_VALUE) {
0052         CloseHandle(h);
0053     }
0054 };
0055 typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(handleCloser)> ScopedHANDLE;
0056 
0057 /** Make sure the NetApi functions are called with the correct level argument (for template functions)
0058  * This argument can be retrieved by using NetApiTypeInfo<T>::level. In order to do so the type must be
0059  * registered by writing e.g. NETAPI_TYPE_INFO(GROUP_INFO, 0) for GROUP_INFO_0
0060  */
0061 template<typename T>
0062 struct NetApiTypeInfo {
0063 };
0064 #define NETAPI_TYPE_INFO(prefix, n)                                                                                                                            \
0065     template<>                                                                                                                                                 \
0066     struct NetApiTypeInfo<prefix##_##n> {                                                                                                                      \
0067         enum { level = n };                                                                                                                                    \
0068     };
0069 NETAPI_TYPE_INFO(GROUP_INFO, 0)
0070 NETAPI_TYPE_INFO(GROUP_INFO, 3)
0071 NETAPI_TYPE_INFO(USER_INFO, 0)
0072 NETAPI_TYPE_INFO(USER_INFO, 4)
0073 NETAPI_TYPE_INFO(USER_INFO, 11)
0074 NETAPI_TYPE_INFO(GROUP_USERS_INFO, 0)
0075 
0076 // T must be a USER_INFO_* structure
0077 template<typename T>
0078 ScopedNetApiBuffer<T> getUserInfo(LPCWSTR server, const QString &userName, NET_API_STATUS *errCode)
0079 {
0080     LPBYTE userInfoTmp = nullptr;
0081     // if level = 11 a USER_INFO_11 structure gets filled in and allocated by NetUserGetInfo(), etc.
0082     NET_API_STATUS status = NetUserGetInfo(server, (LPCWSTR)userName.utf16(), NetApiTypeInfo<T>::level, &userInfoTmp);
0083     if (status != NERR_Success) {
0084         userInfoTmp = nullptr;
0085     }
0086     if (errCode) {
0087         *errCode = status;
0088     }
0089     return ScopedNetApiBuffer<T>((T *)userInfoTmp);
0090 }
0091 
0092 // enumeration functions
0093 /** simplify calling the Net*Enum functions to prevent copy and paste for allUsers(), allUserNames(), allGroups(), allGroupNames()
0094  * @tparam T The type that is enumerated (e.g. USER_INFO_11) Must be registered using NETAPI_TYPE_INFO.
0095  * @param callback Callback for each listed object. Signature: void(const T&)
0096  * @param enumFunc This function enumerates the data using a Net* function.
0097  * It will be called in a loop as long as it returns ERROR_MORE_DATA.
0098  *
0099  */
0100 template<class T, class Callback, class EnumFunction>
0101 static void netApiEnumerate(uint maxCount, Callback callback, EnumFunction enumFunc)
0102 {
0103     NET_API_STATUS nStatus = NERR_Success;
0104     DWORD_PTR resumeHandle = 0;
0105     uint total = 0;
0106     int level = NetApiTypeInfo<T>::level;
0107     do {
0108         LPBYTE buffer = nullptr;
0109         DWORD entriesRead = 0;
0110         DWORD totalEntries = 0;
0111         nStatus = enumFunc(level, &buffer, &entriesRead, &totalEntries, &resumeHandle);
0112         // qDebug("Net*Enum(level = %d) returned %d entries, total was (%d), status = %d, resume handle = %llx",
0113         //    level, entriesRead, totalEntries, nStatus, resumeHandle);
0114 
0115         // buffer must always be freed, even if Net*Enum fails
0116         ScopedNetApiBuffer<T> groupInfo((T *)buffer);
0117         if (nStatus == NERR_Success || nStatus == ERROR_MORE_DATA) {
0118             for (DWORD i = 0; total < maxCount && i < entriesRead; i++, total++) {
0119                 callback(groupInfo.get()[i]);
0120             }
0121         } else {
0122             qCWarning(KCOREADDONS_DEBUG, "NetApi enumerate function failed: status = %d", (int)nStatus);
0123         }
0124     } while (nStatus == ERROR_MORE_DATA);
0125 }
0126 
0127 template<class T, class Callback>
0128 void enumerateAllUsers(uint maxCount, Callback callback)
0129 {
0130     netApiEnumerate<T>(maxCount, callback, [](int level, LPBYTE *buffer, DWORD *count, DWORD *total, PDWORD_PTR resumeHandle) {
0131         // pass 0 as filter -> get all users
0132         // Why does this function take a DWORD* as resume handle and NetUserEnum/NetGroupGetUsers a UINT64*
0133         // Great API design by Microsoft...
0134         // casting the uint64* to uint32* is fine, it just writes to the first 32 bits
0135         return NetUserEnum(nullptr, level, 0, buffer, MAX_PREFERRED_LENGTH, count, total, (PDWORD)resumeHandle);
0136     });
0137 }
0138 
0139 template<typename T, class Callback>
0140 void enumerateAllGroups(uint maxCount, Callback callback)
0141 {
0142     netApiEnumerate<T>(maxCount, callback, [](int level, LPBYTE *buffer, DWORD *count, DWORD *total, PDWORD_PTR resumeHandle) {
0143         return NetGroupEnum(nullptr, level, buffer, MAX_PREFERRED_LENGTH, count, total, resumeHandle);
0144     });
0145 }
0146 
0147 template<typename T, class Callback>
0148 void enumerateGroupsForUser(uint maxCount, const QString &name, Callback callback)
0149 {
0150     LPCWSTR nameStr = (LPCWSTR)name.utf16();
0151     netApiEnumerate<T>(maxCount, callback, [&](int level, LPBYTE *buffer, DWORD *count, DWORD *total, PDWORD_PTR resumeHandle) -> NET_API_STATUS {
0152         Q_UNUSED(resumeHandle);
0153         NET_API_STATUS ret = NetUserGetGroups(nullptr, nameStr, level, buffer, MAX_PREFERRED_LENGTH, count, total);
0154         // if we return ERROR_MORE_DATA here it will result in an endless loop
0155         if (ret == ERROR_MORE_DATA) {
0156             qCWarning(KCOREADDONS_DEBUG) << "NetUserGetGroups for user" << name << "returned ERROR_MORE_DATA. This should not happen!";
0157             ret = NERR_Success;
0158         }
0159         return ret;
0160     });
0161 }
0162 
0163 template<typename T, class Callback>
0164 void enumerateUsersForGroup(const QString &name, uint maxCount, Callback callback)
0165 {
0166     LPCWSTR nameStr = (LPCWSTR)name.utf16();
0167     netApiEnumerate<T>(maxCount, callback, [nameStr](int level, LPBYTE *buffer, DWORD *count, DWORD *total, PDWORD_PTR resumeHandle) {
0168         return NetGroupGetUsers(nullptr, nameStr, level, buffer, MAX_PREFERRED_LENGTH, count, total, resumeHandle);
0169     });
0170 }
0171 
0172 class KUserPrivate : public QSharedData
0173 {
0174     typedef QExplicitlySharedDataPointer<KUserPrivate> Ptr;
0175     KUserPrivate()
0176         : isAdmin(false)
0177     {
0178     }
0179     // takes ownership over userInfo_
0180     KUserPrivate(KUserId uid, KGroupId gid, const QString &loginName, const QString &fullName, const QString &domain, const QString &homeDir, bool isAdmin)
0181         : uid(uid)
0182         , gid(gid)
0183         , loginName(loginName)
0184         , fullName(fullName)
0185         , domain(domain)
0186         , homeDir(homeDir)
0187         , isAdmin(isAdmin)
0188     {
0189         Q_ASSERT(uid.isValid());
0190     }
0191     static QString guessHomeDir(const QString &username, KUserId uid)
0192     {
0193         // usri11_home_dir/usri4_home_dir is often empty
0194         // check whether it is the homedir for the current user and if not then fall back to "<user profiles dir>\<user name>"
0195         if (uid == KUserId::currentUserId()) {
0196             return QDir::homePath();
0197         }
0198         QString homeDir;
0199         WCHAR profileDirPath[MAX_PATH];
0200         DWORD bufSize = MAX_PATH;
0201         BOOL result = GetProfilesDirectoryW(profileDirPath, &bufSize);
0202         if (result) {
0203             // This might not be correct: e.g. with local user and domain user with same
0204             // In that case it could be C:\Users\Foo (local user) vs C:\Users\Foo.DOMAIN (domain user)
0205             // However it is still much better than the previous code which just returned the current users home dir
0206             homeDir = QString::fromWCharArray(profileDirPath) + QLatin1Char('\\') + username;
0207         }
0208         return homeDir;
0209     }
0210 
0211 public:
0212     static Ptr sharedNull;
0213     KUserId uid;
0214     KGroupId gid;
0215     QString loginName;
0216     QString fullName;
0217     QString domain;
0218     QString homeDir;
0219     bool isAdmin;
0220 
0221     /** Creates a user info from a SID (never returns null) */
0222     static Ptr create(KUserId uid)
0223     {
0224         if (!uid.isValid()) {
0225             return sharedNull;
0226         }
0227         // now find the fully qualified name for the user
0228         DWORD nameBufferLen = UNLEN + 1;
0229         WCHAR nameBuffer[UNLEN + 1];
0230         DWORD domainBufferLen = UNLEN + 1;
0231         WCHAR domainBuffer[UNLEN + 1];
0232         SID_NAME_USE use;
0233         if (!LookupAccountSidW(nullptr, uid.nativeId(), nameBuffer, &nameBufferLen, domainBuffer, &domainBufferLen, &use)) {
0234             qCWarning(KCOREADDONS_DEBUG) << "Could not lookup user " << uid.toString() << "error =" << GetLastError();
0235             return sharedNull;
0236         }
0237         QString loginName = QString::fromWCharArray(nameBuffer);
0238         QString domainName = QString::fromWCharArray(domainBuffer);
0239         if (use != SidTypeUser && use != SidTypeDeletedAccount) {
0240             qCWarning(KCOREADDONS_DEBUG).nospace() << "SID for " << domainName << "\\" << loginName << " (" << uid.toString() << ") is not of type user ("
0241                                                    << SidTypeUser << " or " << SidTypeDeletedAccount << "). Got type " << use << " instead.";
0242             return sharedNull;
0243         }
0244         // now get the server name to query (could be null for local machine)
0245         LPWSTR servernameTmp = nullptr;
0246         NET_API_STATUS status = NetGetAnyDCName(nullptr, 0, (LPBYTE *)&servernameTmp);
0247         if (status != NERR_Success) {
0248             // this always fails on my desktop system, don't spam the output
0249             // qDebug("NetGetAnyDCName failed with error %d", status);
0250         }
0251         ScopedNetApiBuffer<WCHAR> servername(servernameTmp);
0252 
0253         QString fullName;
0254         QString homeDir;
0255         KGroupId group;
0256         bool isAdmin = false;
0257         // must NOT pass the qualified name ("domain\user") here or lookup fails -> just the name
0258         // try USER_INFO_4 first, MSDN says it is valid only on servers (whatever that means), it works on my desktop system
0259         // If it fails fall back to USER_INFO11, which has all the needed information except primary group
0260         if (auto userInfo4 = getUserInfo<USER_INFO_4>(servername.get(), loginName, &status)) {
0261             Q_ASSERT(KUserId(userInfo4->usri4_user_sid) == uid); // if this is not the same we have a logic error
0262             fullName = QString::fromWCharArray(userInfo4->usri4_full_name);
0263             homeDir = QString::fromWCharArray(userInfo4->usri4_home_dir);
0264             isAdmin = userInfo4->usri4_priv == USER_PRIV_ADMIN;
0265             // now determine the primary group:
0266             const DWORD primaryGroup = userInfo4->usri4_primary_group_id;
0267             // primary group is a relative identifier, i.e. in order to get the SID for that group
0268             // we have to take the user SID and replace the last subauthority value with the relative identifier
0269             group = KGroupId(uid.nativeId()); // constructor does not check whether the sid refers to a group
0270             Q_ASSERT(group.isValid());
0271             UCHAR numSubauthorities = *GetSidSubAuthorityCount(group.nativeId());
0272             PDWORD lastSubAutority = GetSidSubAuthority(group.nativeId(), numSubauthorities - 1);
0273             *lastSubAutority = primaryGroup;
0274         } else if (auto userInfo11 = getUserInfo<USER_INFO_11>(servername.get(), loginName, &status)) {
0275             fullName = QString::fromWCharArray(userInfo11->usri11_full_name);
0276             homeDir = QString::fromWCharArray(userInfo11->usri11_home_dir);
0277             isAdmin = userInfo11->usri11_priv == USER_PRIV_ADMIN;
0278         } else {
0279             qCWarning(KCOREADDONS_DEBUG).nospace() << "Could not get information for user " << domainName << "\\" << loginName << ": error code = " << status;
0280             return sharedNull;
0281         }
0282         if (homeDir.isEmpty()) {
0283             homeDir = guessHomeDir(loginName, uid);
0284         }
0285         // if we couldn't find a primary group just take the first group found for this user
0286         if (!group.isValid()) {
0287             enumerateGroupsForUser<GROUP_USERS_INFO_0>(1, loginName, [&](const GROUP_USERS_INFO_0 &info) {
0288                 group = KGroupId::fromName(QString::fromWCharArray(info.grui0_name));
0289             });
0290         }
0291         return Ptr(new KUserPrivate(uid, group, loginName, fullName, domainName, homeDir, isAdmin));
0292     }
0293 };
0294 
0295 KUserPrivate::Ptr KUserPrivate::sharedNull(new KUserPrivate());
0296 
0297 KUser::KUser(UIDMode mode)
0298 {
0299     if (mode == UseEffectiveUID) {
0300         d = KUserPrivate::create(KUserId::currentEffectiveUserId());
0301     } else if (mode == UseRealUserID) {
0302         d = KUserPrivate::create(KUserId::currentUserId());
0303     } else {
0304         d = KUserPrivate::sharedNull;
0305     }
0306 }
0307 
0308 KUser::KUser(K_UID uid)
0309     : d(KUserPrivate::create(KUserId(uid)))
0310 {
0311 }
0312 
0313 KUser::KUser(KUserId uid)
0314     : d(KUserPrivate::create(uid))
0315 {
0316 }
0317 
0318 KUser::KUser(const QString &name)
0319     : d(KUserPrivate::create(KUserId::fromName(name)))
0320 {
0321 }
0322 
0323 KUser::KUser(const char *name)
0324     : d(KUserPrivate::create(KUserId::fromName(QString::fromLocal8Bit(name))))
0325 {
0326 }
0327 
0328 KUser::KUser(const KUser &user)
0329     : d(user.d)
0330 {
0331 }
0332 
0333 KUser &KUser::operator=(const KUser &user)
0334 {
0335     d = user.d;
0336     return *this;
0337 }
0338 
0339 bool KUser::operator==(const KUser &user) const
0340 {
0341     return isValid() && d->uid == user.d->uid;
0342 }
0343 
0344 bool KUser::isValid() const
0345 {
0346     return d->uid.isValid();
0347 }
0348 
0349 bool KUser::isSuperUser() const
0350 {
0351     return d->isAdmin;
0352 }
0353 
0354 QString KUser::loginName() const
0355 {
0356     return d->loginName;
0357 }
0358 
0359 QString KUser::homeDir() const
0360 {
0361     return d->homeDir;
0362 }
0363 
0364 // Some RAII objects to help uninitializing/destroying WinAPI stuff
0365 // used in faceIconPath.
0366 class COMInitializer
0367 {
0368 public:
0369     COMInitializer()
0370         : result(CoInitialize(nullptr))
0371     {
0372     }
0373     ~COMInitializer()
0374     {
0375         if (SUCCEEDED(result)) {
0376             CoUninitialize();
0377         }
0378     }
0379     HRESULT result;
0380 };
0381 class W32Library
0382 {
0383 public:
0384     W32Library(HMODULE h)
0385         : h(h)
0386     {
0387     }
0388     ~W32Library()
0389     {
0390         if (h) {
0391             FreeLibrary(h);
0392         }
0393     }
0394     operator HMODULE()
0395     {
0396         return h;
0397     }
0398     HMODULE h;
0399 };
0400 
0401 // faceIconPath uses undocumented Windows API known as SHGetUserPicturePath,
0402 // only accessible by ordinal, unofficially documented at
0403 // http://undoc.airesoft.co.uk/shell32.dll/SHGetUserPicturePath.php
0404 
0405 // The function has a different ordinal and parameters on Windows XP and Vista/7.
0406 // These structs encapsulate the differences.
0407 
0408 struct FaceIconPath_XP {
0409     typedef HRESULT(WINAPI *funcptr_t)(LPCWSTR, DWORD, LPWSTR);
0410     static const int ordinal = 233;
0411     static HRESULT getPicturePath(funcptr_t SHGetUserPicturePathXP, LPCWSTR username, LPWSTR buf, UINT bufsize)
0412     {
0413         Q_UNUSED(bufsize);
0414         // assumes the buffer is MAX_PATH in size
0415         return SHGetUserPicturePathXP(username, 0, buf);
0416     }
0417 };
0418 struct FaceIconPath_Vista {
0419     typedef HRESULT(WINAPI *funcptr_t)(LPCWSTR, DWORD, LPWSTR, UINT);
0420     static const int ordinal = 261;
0421     static HRESULT getPicturePath(funcptr_t SHGetUserPicturePathV, LPCWSTR username, LPWSTR buf, UINT bufsize)
0422     {
0423         return SHGetUserPicturePathV(username, 0, buf, bufsize);
0424     }
0425 };
0426 
0427 template<typename Platform>
0428 static QString faceIconPathImpl(LPCWSTR username)
0429 {
0430     static COMInitializer COMinit;
0431 
0432     static W32Library shellMod = LoadLibraryA("shell32.dll");
0433     if (!shellMod) {
0434         return QString();
0435     }
0436     static typename Platform::funcptr_t sgupp_ptr =
0437         reinterpret_cast<typename Platform::funcptr_t>(GetProcAddress(shellMod, MAKEINTRESOURCEA(Platform::ordinal)));
0438     if (!sgupp_ptr) {
0439         return QString();
0440     }
0441 
0442     WCHAR pathBuf[MAX_PATH];
0443 
0444     HRESULT res = Platform::getPicturePath(sgupp_ptr, username, pathBuf, MAX_PATH);
0445     if (res != S_OK) {
0446         return QString();
0447     }
0448     return QString::fromWCharArray(pathBuf);
0449 }
0450 
0451 QString KUser::faceIconPath() const
0452 {
0453     if (!isValid()) {
0454         return QString();
0455     }
0456 
0457     LPCWSTR username = reinterpret_cast<const WCHAR *>(d->loginName.utf16());
0458     return faceIconPathImpl<FaceIconPath_Vista>(username);
0459 }
0460 
0461 QString KUser::shell() const
0462 {
0463     return isValid() ? QStringLiteral("cmd.exe") : QString();
0464 }
0465 
0466 KUserId KUser::userId() const
0467 {
0468     return d->uid;
0469 }
0470 
0471 KGroupId KUser::groupId() const
0472 {
0473     return d->gid;
0474 }
0475 
0476 QVariant KUser::property(UserProperty which) const
0477 {
0478     if (which == FullName) {
0479         return QVariant(d->fullName);
0480     }
0481 
0482     return QVariant();
0483 }
0484 
0485 KUser::~KUser()
0486 {
0487 }
0488 
0489 class KUserGroupPrivate : public QSharedData
0490 {
0491 public:
0492     QString name;
0493     KGroupId gid;
0494     KUserGroupPrivate()
0495     {
0496     }
0497     KUserGroupPrivate(const QString &name, KGroupId id)
0498         : name(name)
0499         , gid(id)
0500     {
0501         if (!name.isEmpty()) {
0502             PBYTE groupInfoTmp = nullptr;
0503             NET_API_STATUS status = NetGroupGetInfo(nullptr, (LPCWSTR)name.utf16(), 0, &groupInfoTmp);
0504             // must always be freed, even on error
0505             ScopedNetApiBuffer<GROUP_INFO_0> groupInfo((GROUP_INFO_0 *)groupInfoTmp);
0506             if (status != NERR_Success) {
0507                 qCWarning(KCOREADDONS_DEBUG) << "Failed to find group with name" << name << "error =" << status;
0508                 groupInfo.reset();
0509             }
0510             if (!id.isValid()) {
0511                 gid = KGroupId::fromName(name);
0512             }
0513         }
0514     }
0515 };
0516 
0517 KUserGroup::KUserGroup(const QString &_name)
0518     : d(new KUserGroupPrivate(_name, KGroupId()))
0519 {
0520 }
0521 
0522 KUserGroup::KUserGroup(const char *_name)
0523     : d(new KUserGroupPrivate(QLatin1String(_name), KGroupId()))
0524 {
0525 }
0526 
0527 static QString nameFromGroupId(KGroupId gid)
0528 {
0529     if (!gid.isValid()) {
0530         return QString();
0531     }
0532 
0533     DWORD bufferLen = UNLEN + 1;
0534     WCHAR buffer[UNLEN + 1];
0535     DWORD domainBufferLen = UNLEN + 1;
0536     WCHAR domainBuffer[UNLEN + 1];
0537     SID_NAME_USE eUse;
0538     QString name;
0539     if (LookupAccountSidW(NULL, gid.nativeId(), buffer, &bufferLen, domainBuffer, &domainBufferLen, &eUse)) {
0540         if (eUse == SidTypeGroup || eUse == SidTypeWellKnownGroup) {
0541             name = QString::fromWCharArray(buffer);
0542         } else {
0543             qCWarning(KCOREADDONS_DEBUG) << QString::fromWCharArray(buffer) << "is not a group, SID type is" << eUse;
0544         }
0545     }
0546     return name;
0547 }
0548 
0549 KUserGroup::KUserGroup(KGroupId gid)
0550     : d(new KUserGroupPrivate(nameFromGroupId(gid), gid))
0551 {
0552 }
0553 
0554 KUserGroup::KUserGroup(K_GID gid)
0555 {
0556     KGroupId groupId(gid);
0557     d = new KUserGroupPrivate(nameFromGroupId(groupId), groupId);
0558 }
0559 
0560 KUserGroup::KUserGroup(KUser::UIDMode mode)
0561 {
0562     KGroupId gid;
0563     if (mode == KUser::UseEffectiveUID) {
0564         gid = KGroupId::currentGroupId();
0565     } else if (mode == KUser::UseRealUserID) {
0566         gid = KGroupId::currentEffectiveGroupId();
0567     }
0568     d = new KUserGroupPrivate(nameFromGroupId(gid), gid);
0569 }
0570 
0571 KUserGroup::KUserGroup(const KUserGroup &group)
0572     : d(group.d)
0573 {
0574 }
0575 
0576 KUserGroup &KUserGroup::operator=(const KUserGroup &group)
0577 {
0578     d = group.d;
0579     return *this;
0580 }
0581 
0582 bool KUserGroup::operator==(const KUserGroup &group) const
0583 {
0584     return isValid() && d->gid == group.d->gid && d->name == group.d->name;
0585 }
0586 
0587 bool KUserGroup::isValid() const
0588 {
0589     return d->gid.isValid() && !d->name.isEmpty();
0590 }
0591 
0592 QString KUserGroup::name() const
0593 {
0594     return d->name;
0595 }
0596 
0597 KGroupId KUserGroup::groupId() const
0598 {
0599     return d->gid;
0600 }
0601 
0602 KUserGroup::~KUserGroup()
0603 {
0604 }
0605 
0606 QList<KUser> KUser::allUsers(uint maxCount)
0607 {
0608     QList<KUser> result;
0609     // No advantage if we take a USER_INFO_11, since there is no way of copying it
0610     // and it is not owned by this function!
0611     // -> get a USER_INFO_0 instead and then use KUser(QString)
0612     // USER_INFO_23 or USER_INFO_23 would be ideal here since they contains a SID,
0613     // but that fails with error code 0x7c (bad level)
0614     enumerateAllUsers<USER_INFO_0>(maxCount, [&result](const USER_INFO_0 &info) {
0615         result.append(KUser(QString::fromWCharArray(info.usri0_name)));
0616     });
0617     return result;
0618 }
0619 
0620 QStringList KUser::allUserNames(uint maxCount)
0621 {
0622     QStringList result;
0623     enumerateAllUsers<USER_INFO_0>(maxCount, [&result](const USER_INFO_0 &info) {
0624         result.append(QString::fromWCharArray(info.usri0_name));
0625     });
0626     return result;
0627 }
0628 
0629 QList<KUserGroup> KUserGroup::allGroups(uint maxCount)
0630 {
0631     QList<KUserGroup> result;
0632     // MSDN documentation say 3 is a valid level, however the function fails with invalid level!!!
0633     // User GROUP_INFO_0 instead...
0634     enumerateAllGroups<GROUP_INFO_0>(maxCount, [&result](const GROUP_INFO_0 &groupInfo) {
0635         result.append(KUserGroup(QString::fromWCharArray(groupInfo.grpi0_name)));
0636     });
0637     return result;
0638 }
0639 
0640 QStringList KUserGroup::allGroupNames(uint maxCount)
0641 {
0642     QStringList result;
0643     enumerateAllGroups<GROUP_INFO_0>(maxCount, [&result](const GROUP_INFO_0 &groupInfo) {
0644         result.append(QString::fromWCharArray(groupInfo.grpi0_name));
0645     });
0646     return result;
0647 }
0648 
0649 QList<KUserGroup> KUser::groups(uint maxCount) const
0650 {
0651     QList<KUserGroup> result;
0652     if (!isValid()) {
0653         return result;
0654     }
0655     enumerateGroupsForUser<GROUP_USERS_INFO_0>(maxCount, d->loginName, [&result](const GROUP_USERS_INFO_0 &info) {
0656         result.append(KUserGroup(QString::fromWCharArray(info.grui0_name)));
0657     });
0658     return result;
0659 }
0660 
0661 QStringList KUser::groupNames(uint maxCount) const
0662 {
0663     QStringList result;
0664     if (!isValid()) {
0665         return result;
0666     }
0667     enumerateGroupsForUser<GROUP_USERS_INFO_0>(maxCount, d->loginName, [&result](const GROUP_USERS_INFO_0 &info) {
0668         result.append(QString::fromWCharArray(info.grui0_name));
0669     });
0670     return result;
0671 }
0672 
0673 QList<KUser> KUserGroup::users(uint maxCount) const
0674 {
0675     QList<KUser> result;
0676     if (!isValid()) {
0677         return result;
0678     }
0679     enumerateGroupsForUser<GROUP_USERS_INFO_0>(maxCount, d->name, [&result](const GROUP_USERS_INFO_0 &info) {
0680         result.append(KUser(QString::fromWCharArray(info.grui0_name)));
0681     });
0682     return result;
0683 }
0684 
0685 QStringList KUserGroup::userNames(uint maxCount) const
0686 {
0687     QStringList result;
0688     if (!isValid()) {
0689         return result;
0690     }
0691     enumerateGroupsForUser<GROUP_USERS_INFO_0>(maxCount, d->name, [&result](const GROUP_USERS_INFO_0 &info) {
0692         result.append(QString::fromWCharArray(info.grui0_name));
0693     });
0694     return result;
0695 }
0696 
0697 static const auto invalidSidString = QStringLiteral("<invalid SID>");
0698 
0699 static QString sidToString(void *sid)
0700 {
0701     if (!sid || !IsValidSid(sid)) {
0702         return invalidSidString;
0703     }
0704     WCHAR *sidStr; // allocated by ConvertStringSidToSidW, must be freed using LocalFree()
0705     if (!ConvertSidToStringSidW(sid, &sidStr)) {
0706         return invalidSidString;
0707     }
0708     QString ret = QString::fromWCharArray(sidStr);
0709     LocalFree(sidStr);
0710     return ret;
0711 }
0712 
0713 struct WindowsSIDWrapper : public QSharedData {
0714     char sidBuffer[SECURITY_MAX_SID_SIZE];
0715     /** @return a copy of @p sid or null if sid is not valid or an error occurs */
0716     static WindowsSIDWrapper *copySid(PSID sid)
0717     {
0718         if (!sid || !IsValidSid(sid)) {
0719             return nullptr;
0720         }
0721         // create a copy of sid
0722         WindowsSIDWrapper *copy = new WindowsSIDWrapper();
0723         bool success = CopySid(SECURITY_MAX_SID_SIZE, copy->sidBuffer, sid);
0724         if (!success) {
0725             QString sidString = sidToString(sid);
0726             qCWarning(KCOREADDONS_DEBUG, "Failed to copy SID %s, error = %d", qPrintable(sidString), (int)GetLastError());
0727             delete copy;
0728             return nullptr;
0729         }
0730         return copy;
0731     }
0732 };
0733 
0734 template<>
0735 KUserOrGroupId<void *>::KUserOrGroupId()
0736 {
0737 }
0738 
0739 template<>
0740 KUserOrGroupId<void *>::~KUserOrGroupId()
0741 {
0742 }
0743 
0744 template<>
0745 KUserOrGroupId<void *>::KUserOrGroupId(const KUserOrGroupId<void *> &other)
0746     : data(other.data)
0747 {
0748 }
0749 
0750 template<>
0751 inline KUserOrGroupId<void *> &KUserOrGroupId<void *>::operator=(const KUserOrGroupId<void *> &other)
0752 {
0753     data = other.data;
0754     return *this;
0755 }
0756 
0757 template<>
0758 KUserOrGroupId<void *>::KUserOrGroupId(void *nativeId)
0759     : data(WindowsSIDWrapper::copySid(nativeId))
0760 {
0761 }
0762 
0763 template<>
0764 bool KUserOrGroupId<void *>::isValid() const
0765 {
0766     return data;
0767 }
0768 
0769 template<>
0770 void *KUserOrGroupId<void *>::nativeId() const
0771 {
0772     if (!data) {
0773         return nullptr;
0774     }
0775     return data->sidBuffer;
0776 }
0777 
0778 template<>
0779 bool KUserOrGroupId<void *>::operator==(const KUserOrGroupId<void *> &other) const
0780 {
0781     if (data) {
0782         if (!other.data) {
0783             return false;
0784         }
0785         return EqualSid(data->sidBuffer, other.data->sidBuffer);
0786     }
0787     return !other.data; // only equal if other data is also invalid
0788 }
0789 
0790 template<>
0791 bool KUserOrGroupId<void *>::operator!=(const KUserOrGroupId<void *> &other) const
0792 {
0793     return !(*this == other);
0794 }
0795 
0796 template<>
0797 QString KUserOrGroupId<void *>::toString() const
0798 {
0799     return sidToString(data ? data->sidBuffer : nullptr);
0800 }
0801 
0802 /** T must be either KUserId or KGroupId, Callback has signature T(PSID, SID_NAME_USE) */
0803 template<class T, class Callback>
0804 static T sidFromName(const QString &name, Callback callback)
0805 {
0806     if (name.isEmpty()) {
0807         // for some reason empty string will always return S-1-5-32 which is of type domain
0808         // we only want users or groups -> return invalid
0809         return T();
0810     }
0811     char buffer[SECURITY_MAX_SID_SIZE];
0812     DWORD sidLength = SECURITY_MAX_SID_SIZE;
0813     // ReferencedDomainName must be passed or LookupAccountNameW fails
0814     // Documentation says it is optional, however if not passed the function fails and returns the required size
0815     WCHAR domainBuffer[1024];
0816     DWORD domainBufferSize = 1024;
0817     SID_NAME_USE sidType;
0818     bool ok = LookupAccountNameW(nullptr, (LPCWSTR)name.utf16(), buffer, &sidLength, domainBuffer, &domainBufferSize, &sidType);
0819     if (!ok) {
0820         qCWarning(KCOREADDONS_DEBUG) << "Failed to lookup account" << name << "error code =" << GetLastError();
0821         return T();
0822     }
0823     return callback(buffer, sidType);
0824 }
0825 
0826 KUserId KUserId::fromName(const QString &name)
0827 {
0828     return sidFromName<KUserId>(name, [&](PSID sid, SID_NAME_USE sidType) -> KUserId {
0829         if (sidType != SidTypeUser && sidType != SidTypeDeletedAccount) {
0830             qCWarning(KCOREADDONS_DEBUG).nospace() << "Failed to lookup user name " << name << ": resulting SID " << sidToString(sid)
0831                                                    << " is not a user."
0832                                                       " Got SID type "
0833                                                    << sidType << " instead.";
0834             return KUserId();
0835         }
0836         return KUserId(sid);
0837     });
0838 }
0839 
0840 KGroupId KGroupId::fromName(const QString &name)
0841 {
0842     return sidFromName<KGroupId>(name, [&](PSID sid, SID_NAME_USE sidType) -> KGroupId {
0843         if (sidType != SidTypeGroup && sidType != SidTypeWellKnownGroup) {
0844             qCWarning(KCOREADDONS_DEBUG).nospace() << "Failed to lookup user name " << name << ": resulting SID " << sidToString(sid)
0845                                                    << " is not a group."
0846                                                       " Got SID type "
0847                                                    << sidType << " instead.";
0848             return KGroupId();
0849         }
0850         return KGroupId(sid);
0851     });
0852 }
0853 
0854 static std::unique_ptr<char[]> queryProcessInformation(TOKEN_INFORMATION_CLASS type)
0855 {
0856     HANDLE _token;
0857     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &_token)) {
0858         qCWarning(KCOREADDONS_DEBUG, "Failed to get the token for the current process: %d", (int)GetLastError());
0859         return nullptr;
0860     }
0861     ScopedHANDLE token(_token, handleCloser);
0862     // query required size
0863     DWORD requiredSize;
0864     if (!GetTokenInformation(token.get(), type, nullptr, 0, &requiredSize)) {
0865         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
0866             qCWarning(KCOREADDONS_DEBUG, "Failed to get the required size for the token information %d: %d", type, (int)GetLastError());
0867             return nullptr;
0868         }
0869     }
0870     std::unique_ptr<char[]> buffer(new char[requiredSize]);
0871     if (!GetTokenInformation(token.get(), type, buffer.get(), requiredSize, &requiredSize)) {
0872         qCWarning(KCOREADDONS_DEBUG, "Failed to get token information %d from current process: %d", type, (int)GetLastError());
0873         return nullptr;
0874     }
0875     return buffer;
0876 }
0877 
0878 KUserId KUserId::currentUserId()
0879 {
0880     std::unique_ptr<char[]> userTokenBuffer = queryProcessInformation(TokenUser);
0881     TOKEN_USER *userToken = (TOKEN_USER *)userTokenBuffer.get();
0882     return KUserId(userToken->User.Sid);
0883 }
0884 
0885 KGroupId KGroupId::currentGroupId()
0886 {
0887     std::unique_ptr<char[]> primaryGroupBuffer = queryProcessInformation(TokenPrimaryGroup);
0888     TOKEN_PRIMARY_GROUP *primaryGroup = (TOKEN_PRIMARY_GROUP *)primaryGroupBuffer.get();
0889     return KGroupId(primaryGroup->PrimaryGroup);
0890 }
0891 
0892 KUserId KUserId::currentEffectiveUserId()
0893 {
0894     return currentUserId();
0895 }
0896 
0897 KGroupId KGroupId::currentEffectiveGroupId()
0898 {
0899     return currentGroupId();
0900 }
0901 
0902 KCOREADDONS_EXPORT size_t qHash(const KUserId &id, size_t seed)
0903 {
0904     if (!id.isValid()) {
0905         return seed;
0906     }
0907     // we can't just hash the pointer since equal object must have the same hash -> hash contents
0908     char *sid = (char *)id.nativeId();
0909     return qHash(QByteArray::fromRawData(sid, GetLengthSid(sid)), seed);
0910 }
0911 
0912 KCOREADDONS_EXPORT size_t qHash(const KGroupId &id, size_t seed)
0913 {
0914     if (!id.isValid()) {
0915         return seed;
0916     }
0917     // we can't just hash the pointer since equal object must have the same hash -> hash contents
0918     char *sid = (char *)id.nativeId();
0919     return qHash(QByteArray::fromRawData(sid, GetLengthSid(sid)), seed);
0920 }