Stanislav Panferov
Stanislav Panferov

Reputation: 29

c++ Modify DCOM permissions

I am trying to modify DCOM permissions in my native C++ project, which is COM Server. Following to the documentation (1), (2) and this post, I create new absolute security descriptors from EXPLICIT_ACCESS for AccessPermission and LaunchPermission as described here. Then I convert absolute security descriptors to self-relative using MakeSelfRelativeSD. Resulting self-relative security descriptors I cast to BYTE* and write them to the appropriate registry keys using RegSetValue. But when I then view the DCOM permissions using DCOM Confing, they don't match the programmatically set values. What is wrong?

    enum COM_RIGHTS : DWORD
    {
        COM_NONE            = 0,
        COM_EXECUTE         = 1,
        COM_EXECUTE_LOCAL   = 2,
        COM_EXECUTE_REMOTE  = 4,
        COM_ACTIVATE_LOCAL  = 8,
        COM_ACTIVATE_REMOTE = 16
    };
    template<typename T>
    class ACCOUNT_INFORMATION
    {
    public:
        basic_string<T> strAccountName;
        basic_string<T> strDomainName;
        PSID pSid;
        DWORD dwSidLength;
        SID_NAME_USE sidNameUse;
        DWORD accessRights;
        ACCOUNT_INFORMATION(
            basic_string<T> strNewAccountName = basic_string<T>(),
            basic_string<T> strNewDomainName = basic_string<T>(),
            PSID pNewSid = NULL,
            SID_NAME_USE newSidNameUse = SID_NAME_USE::SidTypeUnknown,
            COM_RIGHTS newAccessRights = COM_RIGHTS::COM_NONE);
    };
    template<typename T>
    using PACCOUNT_INFORMATION = ACCOUNT_INFORMATION<T>*;
    
    vector<PSID> SecurityHelper::LocalGroupsSid = SecurityHelper::GetLocalGroupsSid();

    template<size_t N>
    BOOL SecurityHelper::SetDComPermissionsForAccounts(
        RegistryKey* pregKeyAppId,
        const LPCSTR (&rgszAccountNames)[N],
        const COM_RIGHTS (&rgAccessRights)[N],
        const COM_RIGHTS (&rgLaunchRights)[N])
    {
        return SetDComPermissions(pregKeyAppId, rgszAccountNames, rgAccessRights, rgszAccountNames, rgAccessRights);
    }
    template<size_t N>
    BOOL SecurityHelper::SetDComPermissions(
        HKEY hKeyAppId,
        const LPCSTR (&rgszAccessForAccountNames)[N],
        const COM_RIGHTS (&rgAccessRights)[N],
        const LPCSTR (&rgszLaunchAsAccountNames)[N],
        const COM_RIGHTS (&rgLaunchRights)[N])
    {
        BOOL fIsPermissionsSet = FALSE;
        if(hKeyAppId != NULL)
        {
            size_t size1 = 0, size2 = 0;
            LPCSTR* lpcszAccessForAccountNames = (LPCSTR*)&rgszAccessForAccountNames;
            LPCSTR* lpcszLaunchForAccountNames = (LPCSTR*)&rgszLaunchAsAccountNames;
            DWORD* pAccessRights = (DWORD*)(COM_RIGHTS*)&rgAccessRights;
            DWORD* pLaunchRights = (DWORD*)(COM_RIGHTS*)&rgLaunchRights;
            vector<LPCSTR> vectorAccessForAccountNames(lpcszAccessForAccountNames, lpcszAccessForAccountNames + N);
            vector<LPCSTR> vectorLaunchAsAccountNames(lpcszLaunchForAccountNames, lpcszLaunchForAccountNames + N);
            vector<DWORD> vectorAccessRights(pAccessRights, pAccessRights + N);
            vector<DWORD> vectorLaunchRights(pLaunchRights, pLaunchRights + N);
            pair<BOOL, PSECURITY_DESCRIPTOR> pairSDAccessPermissions =
                CreateSecurityDescriptor(vectorAccessForAccountNames, vectorAccessRights);
            pair<BOOL, PSECURITY_DESCRIPTOR> pairSDLaunchPermissions =
                CreateSecurityDescriptor(vectorLaunchAsAccountNames, vectorLaunchRights);
            if(pairSDAccessPermissions.first && pairSDLaunchPermissions.first)
            {
                DWORD dwSDAccessPermissionsLength = 0;
                DWORD dwSDLaunchPermissionsLength = 0;
                if(!MakeSelfRelativeSD(pairSDAccessPermissions.second, NULL, &dwSDAccessPermissionsLength) && GetLastError() == ERROR_INSUFFICIENT_BUFFER
                    && dwSDAccessPermissionsLength > 0
                    && !MakeSelfRelativeSD(pairSDLaunchPermissions.second, NULL, &dwSDLaunchPermissionsLength) && GetLastError() == ERROR_INSUFFICIENT_BUFFER
                    && dwSDLaunchPermissionsLength > 0)
                {
                    PSECURITY_DESCRIPTOR pSDAccessPermissions = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSDAccessPermissionsLength);
                    PSECURITY_DESCRIPTOR pSDLaunchPermissions = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSDLaunchPermissionsLength);
                    if(MakeSelfRelativeSD(pairSDAccessPermissions.second, pSDAccessPermissions, &dwSDAccessPermissionsLength) && pSDAccessPermissions != NULL
                        && MakeSelfRelativeSD(pairSDLaunchPermissions.second, pSDLaunchPermissions, &dwSDLaunchPermissionsLength) && pSDLaunchPermissions != NULL)
                    {
                        PBYTE bpSDAccessPermissions = (PBYTE)pSDAccessPermissions;
                        PBYTE bpSDLaunchPermissions = (PBYTE)pSDLaunchPermissions;
                        fIsPermissionsSet = RegSetKeyValue(hKeyAppId, NULL, L"AccessPermission", REG_BINARY, (LPCVOID)bpSDAccessPermissions, dwSDAccessPermissionsLength) == ERROR_SUCCESS
                            && RegSetKeyValue(hKeyAppId, NULL, L"LaunchPermission", REG_BINARY, (LPCVOID)bpSDLaunchPermissions, dwSDLaunchPermissionsLength) == ERROR_SUCCESS
                    }
                    LocalFree(pSDAccessPermissions);
                    LocalFree(pSDLaunchPermissions);
                }
            }
        }
        return fIsPermissionsSet;
    }
    PSECURITY_DESCRIPTOR SecurityHelper::CreateSecurityDescriptor(vector<LPCSTR> vectorAccountNames, vector<DWORD> vectorAccessRights)
    {
        return CreateSecurityDescriptor(GetMapAccountInfo(vectorAccountNames, vectorAccessRights));
    }
    PSECURITY_DESCRIPTOR SecurityHelper::CreateSecurityDescriptor(map<PSID, ACCOUNT_INFORMATION<CHAR>> mapAccountInfo)
    {
        PSECURITY_DESCRIPTOR psd = NULL;            
        BOOL fIsCreated = FALSE;
        vector<EXPLICIT_ACCESS> vectorExplicitAccess;
        EXPLICIT_ACCESS ea;
        ea.grfAccessMode = SET_ACCESS;
        ea.grfInheritance = NO_INHERITANCE;
        ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
        PEXPLICIT_ACCESS pExplicitAccess;
        for(const pair<PSID, ACCOUNT_INFORMATION<CHAR>> curPair : mapAccountInfo)
        {
            pExplicitAccess = new EXPLICIT_ACCESS(ea);
            switch(curPair.second.sidNameUse)
            {
            case SID_NAME_USE::SidTypeDomain:
                pExplicitAccess->Trustee.TrusteeType = TRUSTEE_IS_DOMAIN;
                break;
            case SID_NAME_USE::SidTypeComputer:
                pExplicitAccess->Trustee.TrusteeType = TRUSTEE_IS_COMPUTER;
                break;
            case SID_NAME_USE::SidTypeWellKnownGroup:
                pExplicitAccess->Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
                break;
            case SID_NAME_USE::SidTypeGroup:
                pExplicitAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
                break;
            case SID_NAME_USE::SidTypeUser:
                pExplicitAccess->Trustee.TrusteeType = TRUSTEE_IS_USER;
                break;
            case SID_NAME_USE::SidTypeLogonSession:
                break;
            case SID_NAME_USE::SidTypeAlias:
                pExplicitAccess->Trustee.TrusteeType = TRUSTEE_IS_ALIAS;
                break;
            case SID_NAME_USE::SidTypeLabel:
                break;
            case SID_NAME_USE::SidTypeInvalid:
                pExplicitAccess->Trustee.TrusteeType = TRUSTEE_IS_INVALID;
                break;
            case SID_NAME_USE::SidTypeUnknown:
                pExplicitAccess->Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
                break;
            }
            pExplicitAccess->Trustee.ptstrName = (LPTSTR)curPair.second.pSid;
            pExplicitAccess->grfAccessPermissions = curPair.second.accessRights;
            vectorExplicitAccess.push_back(*pExplicitAccess);
        }
        DWORD dwEntriesCount = vectorExplicitAccess.size();
        if(dwEntriesCount == mapAccountInfo.size())
        {
            PACL pDacl = NULL;
            PEXPLICIT_ACCESS pExplicitAccess = new EXPLICIT_ACCESS[dwEntriesCount];
            size_t size = dwEntriesCount * sizeof(EXPLICIT_ACCESS);
            memset(pExplicitAccess, 0, size);
            memcpy(pExplicitAccess, vectorExplicitAccess.data(), size);
            if(SetEntriesInAcl(dwEntriesCount, pExplicitAccess, NULL, &pDacl) == ERROR_SUCCESS && pDacl != NULL)
            {
                psd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
                if(psd != NULL)
                {
                    if(InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION))
                    {
                        BOOL fDaclPresent = TRUE;
                        BOOL fDaclDefaulted = FALSE;
                        fIsCreated = SetSecurityDescriptorDacl(psd, fDaclPresent, pDacl, fDaclDefaulted);
                    }
                    if(!fIsCreated)
                    {
                        LocalFree(psd);
                    }
                }
            }
        }
        return fIsCreated ? psd : NULL;
    }
    map<PSID, ACCOUNT_INFORMATION<CHAR>> SecurityHelper::GetMapAccountInfo(vector<LPCSTR> vectorAccountNames, vector<DWORD> vectorAccessRights)
    {
        map<PSID, ACCOUNT_INFORMATION<CHAR>> mapAccountInfo;
        if(vectorAccountNames.size() > 0 && vectorAccessRights.size() == vectorAccountNames.size())
        {
            PACCOUNT_INFORMATION<CHAR> pAccountInfo = NULL;
            for(int index = 0; index < vectorAccountNames.size(); index++)
            {
                pAccountInfo = SecurityHelper::GetAccountInformationFromNameA(vectorAccountNames[index]);
                if(pAccountInfo != NULL && IsValidSid(pAccountInfo->pSid))
                {
                    pAccountInfo->accessRights = vectorAccessRights[index];
                    mapAccountInfo[pAccountInfo->pSid] = *pAccountInfo;
                }
            }
        }
        return mapAccountInfo;
    }
    vector<PSID> SecurityHelper::GetLocalGroupsSid()
    {
        vector<PSID> vectorLocalGroupsSid;
        PLOCALGROUP_INFO_0 pTmpGroupInfo = NULL;
        DWORD entriesRead = 0;
        DWORD totalEntries = 0;
        NET_API_STATUS status = NetLocalGroupEnum(NULL, 0, (LPBYTE*)&pTmpGroupInfo, MAX_PREFERRED_LENGTH, (LPDWORD)&entriesRead, (LPDWORD)&totalEntries, (PDWORD_PTR)NULL);
        if(status == NERR_Success && pTmpGroupInfo != NULL && entriesRead == totalEntries)
        {
            PLOCALGROUP_INFO_0 pGroupInfo = new LOCALGROUP_INFO_0[entriesRead + 1];
            memset(pGroupInfo, 0, (entriesRead + 1) * sizeof(LOCALGROUP_INFO_0));
            memcpy(pGroupInfo, pTmpGroupInfo, entriesRead * sizeof(LOCALGROUP_INFO_0));
            LPCWSTR lpcwszInteractive = L"ИНТЕРАКТИВНЫЕ";
            size_t dstSize = wcslen(lpcwszInteractive) + 1;
            entriesRead++;
            pGroupInfo[entriesRead-1].lgrpi0_name = new WCHAR[dstSize];
            wcscpy_s(pGroupInfo[entriesRead-1].lgrpi0_name, dstSize, lpcwszInteractive);
            LPWSTR lpwszAccountName = NULL;
            PSID psidCurrent = NULL;
            DWORD dwSidLength = 0;
            LPWSTR lpwszSidCurrent = NULL;
            LPWSTR lpwszDomainName = NULL;
            DWORD dwDomainNameSize = 0;
            SID_NAME_USE sidNameUse;
            for(int index = 0; index < entriesRead; index++)
            {
                if(pGroupInfo[index].lgrpi0_name != NULL)
                {
                    lpwszAccountName = const_cast<LPWSTR>(pGroupInfo[index].lgrpi0_name);
                    psidCurrent = NULL;
                    dwSidLength = 0;
                    lpwszDomainName = NULL;
                    dwDomainNameSize = 0;
                    if(!LookupAccountNameW(NULL, lpwszAccountName, NULL, &dwSidLength, NULL, &dwDomainNameSize, &sidNameUse)
                            && GetLastError() == ERROR_INSUFFICIENT_BUFFER && dwSidLength > 0 && dwDomainNameSize > 0)
                    {
                        psidCurrent = (PSID)new BYTE[dwSidLength];
                        lpwszDomainName = new WCHAR[dwDomainNameSize];
                        if(LookupAccountNameW(NULL, lpwszAccountName, psidCurrent, &dwSidLength, lpwszDomainName, &dwDomainNameSize, &sidNameUse))
                        {
                            lpwszSidCurrent = NULL;
                            ConvertSidToStringSidW(psidCurrent, &lpwszSidCurrent);
                            vectorLocalGroupsSid.push_back(psidCurrent);
                            LocalGroupNamesW.push_back(format(L"{}\\{}", lpwszDomainName, lpwszAccountName));
                            LocalGroupNamesA.push_back(format("{}\\{}", StringHelper::UnicodeToAnsi(lpwszDomainName), StringHelper::UnicodeToAnsi(lpwszAccountName)));
                        }
                    }
                }
            }
        }
        return vectorLocalGroupsSid;
    }
    PACCOUNT_INFORMATION<CHAR> SecurityHelper::GetAccountInformationFromNameA(LPCSTR lpcszAccountName)
    {
        PACCOUNT_INFORMATION<CHAR> pAccountInformation = NULL;
        BOOL fResult = FALSE;
        if(!StringHelper::IsEmptyStringA(lpcszAccountName))
        {
            pAccountInformation = new ACCOUNT_INFORMATION<CHAR>(string(lpcszAccountName));
            DWORD dwDomainNameSize = 0;
            LPSTR lpszDomainName = NULL;
            fResult = LookupAccountNameA(
                NULL,
                lpcszAccountName,
                pAccountInformation->pSid,
                &pAccountInformation->dwSidLength,
                lpszDomainName,
                &dwDomainNameSize,
                &pAccountInformation->sidNameUse);
            if(!fResult && GetLastError() == ERROR_INSUFFICIENT_BUFFER && pAccountInformation->dwSidLength > 0 && dwDomainNameSize > 0)
            {
                pAccountInformation->pSid = new BYTE[pAccountInformation->dwSidLength];
                lpszDomainName = new CHAR[dwDomainNameSize];
                fResult = LookupAccountNameA(
                    NULL,
                    lpcszAccountName,
                    pAccountInformation->pSid,
                    &pAccountInformation->dwSidLength,
                    lpszDomainName,
                    &dwDomainNameSize,
                    &pAccountInformation->sidNameUse);
                if(fResult)
                {
                    if(pAccountInformation->sidNameUse == SidTypeAlias
                        && find_if(LocalGroupsSid.begin(), LocalGroupsSid.end(), 
                            [&](PSID psidCurrent) -> bool { return EqualSid(psidCurrent, pAccountInformation->pSid); } ) != LocalGroupsSid.end())
                    {
                        pAccountInformation->sidNameUse = SidTypeWellKnownGroup;
                    }
                    if(lpszDomainName != NULL && dwDomainNameSize > 0)
                    {
                        pAccountInformation->strDomainName = string(lpszDomainName, dwDomainNameSize);
                    }
                }
            }
        }
        return fResult ? pAccountInformation : NULL;
    }

COM_RIGHTS localAccessRights = (COM_RIGHTS)(COM_RIGHTS::COM_EXECUTE | COM_RIGHTS::COM_EXECUTE_LOCAL);
COM_RIGHTS localLaunchRights = (COM_RIGHTS)(COM_RIGHTS::COM_EXECUTE | COM_RIGHTS::COM_EXECUTE_LOCAL | COM_RIGHTS::COM_ACTIVATE_LOCAL);
COM_RIGHTS fullAccessRights = (COM_RIGHTS)(COM_RIGHTS::COM_EXECUTE | COM_RIGHTS::COM_EXECUTE_LOCAL | COM_RIGHTS::COM_EXECUTE_REMOTE);
COM_RIGHTS fullLaunchRights = (COM_RIGHTS)(COM_RIGHTS::COM_EXECUTE | COM_RIGHTS::COM_EXECUTE_LOCAL | COM_RIGHTS::COM_EXECUTE_REMOTE
                        | COM_RIGHTS::COM_ACTIVATE_LOCAL | COM_RIGHTS::COM_ACTIVATE_REMOTE);
SecurityHelper::SetDComPermissionsForAccounts(
                        pregKeyAppId,
                        { "Пользователи", "ИНТЕРАКТИВНЫЕ", "СИСТЕМА", "Администраторы" },
                        { localAccessRights, localAccessRights, fullAccessRights, fullAccessRights },
                        { localLaunchRights, localLaunchRights, fullLaunchRights, fullLaunchRights }
                        );

I solved this problem when I found two errors. The first is in the function GetAccountInformationFromNameA. For accounts with SID_NAME_USE::SidTypeAlias ​​that are a local groups (belong to vectorLocalGroupsSid), SID_NAME_USE::SidTypeGroup is required. The second error is in the function SetDComPermissionsForAccounts, which is set launch permissions same as access permissions. Below is the corrected code.

    template<size_t N>
    BOOL SecurityHelper::SetDComPermissionsForAccounts(
        RegistryKey* pregKeyAppId,
        const LPCSTR (&rgszAccountNames)[N],
        const COM_RIGHTS (&rgAccessRights)[N],
        const COM_RIGHTS (&rgLaunchRights)[N])
    {
        return SetDComPermissions(pregKeyAppId, rgszAccountNames, rgAccessRights, rgszAccountNames, rgLaunchRights);
    }
    PACCOUNT_INFORMATION<CHAR> SecurityHelper::GetAccountInformationFromNameA(LPCSTR lpcszAccountName)
    {
        PACCOUNT_INFORMATION<CHAR> pAccountInformation = NULL;
        BOOL fResult = FALSE;
        if(!StringHelper::IsEmptyStringA(lpcszAccountName))
        {
            pAccountInformation = new ACCOUNT_INFORMATION<CHAR>(string(lpcszAccountName));
            DWORD dwDomainNameSize = 0;
            LPSTR lpszDomainName = NULL;
            fResult = LookupAccountNameA(
                NULL,
                lpcszAccountName,
                pAccountInformation->pSid,
                &pAccountInformation->dwSidLength,
                lpszDomainName,
                &dwDomainNameSize,
                &pAccountInformation->sidNameUse);
            if(!fResult && GetLastError() == ERROR_INSUFFICIENT_BUFFER && pAccountInformation->dwSidLength > 0 && dwDomainNameSize > 0)
            {
                pAccountInformation->pSid = new BYTE[pAccountInformation->dwSidLength];
                lpszDomainName = new CHAR[dwDomainNameSize];
                fResult = LookupAccountNameA(
                    NULL,
                    lpcszAccountName,
                    pAccountInformation->pSid,
                    &pAccountInformation->dwSidLength,
                    lpszDomainName,
                    &dwDomainNameSize,
                    &pAccountInformation->sidNameUse);
                if(fResult)
                {
                    if(pAccountInformation->sidNameUse == SidTypeAlias
                        && find_if(LocalGroupsSid.begin(), LocalGroupsSid.end(), 
                            [&](PSID psidCurrent) -> bool { return EqualSid(psidCurrent, pAccountInformation->pSid); } ) != LocalGroupsSid.end())
                    {
                        pAccountInformation->sidNameUse = SidTypeGroup;
                    }
                    if(lpszDomainName != NULL && dwDomainNameSize > 0)
                    {
                        pAccountInformation->strDomainName = string(lpszDomainName, dwDomainNameSize);
                    }
                }
            }
        }
        return fResult ? pAccountInformation : NULL;
    }

Upvotes: 0

Views: 40

Answers (0)

Related Questions