winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

Security.cpp (4934B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "winget/Security.h"
      5 #include "AppInstallerLogging.h"
      6 #include "AppInstallerLanguageUtilities.h"
      7 
      8 namespace AppInstaller::Security
      9 {
     10     namespace
     11     {
     12         bool IsSameAuthority(const SID_IDENTIFIER_AUTHORITY& a, const SID_IDENTIFIER_AUTHORITY& b)
     13         {
     14             for (size_t i = 0; i < ARRAYSIZE(a.Value); ++i)
     15             {
     16                 if (a.Value[i] != b.Value[i])
     17                 {
     18                     return false;
     19                 }
     20             }
     21 
     22             return true;
     23         }
     24 
     25         // Helper to impersonate the COM or RPC caller.
     26         struct ImpersonateCOMorRPCCaller
     27         {
     28             static ImpersonateCOMorRPCCaller BeginImpersonation()
     29             {
     30                 return {};
     31             }
     32 
     33             ~ImpersonateCOMorRPCCaller()
     34             {
     35                 if (m_serverSecurity)
     36                 {
     37                     FAIL_FAST_IF_FAILED(m_serverSecurity->RevertToSelf());
     38                 }
     39                 else
     40                 {
     41                     FAIL_FAST_IF(RpcRevertToSelf() != RPC_S_OK);
     42                 }
     43             }
     44 
     45         private:
     46             ImpersonateCOMorRPCCaller()
     47             {
     48                 if (SUCCEEDED_LOG(CoGetCallContext(IID_IServerSecurity, m_serverSecurity.put_void())))
     49                 {
     50                     THROW_IF_FAILED(m_serverSecurity->ImpersonateClient());
     51                 }
     52                 else
     53                 {
     54                     RPC_STATUS status = RpcImpersonateClient(nullptr);
     55                     THROW_HR_IF(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_RPC, status), status != RPC_S_OK);
     56                 }
     57             }
     58 
     59             wil::com_ptr<IServerSecurity> m_serverSecurity;
     60         };
     61     }
     62 
     63     IntegrityLevel GetEffectiveIntegrityLevel()
     64     {
     65         auto currentIntegrityLevel = wil::get_token_information<TOKEN_MANDATORY_LABEL>();
     66         PSID sid = currentIntegrityLevel->Label.Sid;
     67         THROW_HR_IF(CO_E_INVALIDSID, !IsValidSid(sid));
     68 
     69         auto identifierAuthority = GetSidIdentifierAuthority(sid);
     70         THROW_HR_IF(E_UNEXPECTED, !IsSameAuthority(*identifierAuthority, SECURITY_MANDATORY_LABEL_AUTHORITY));
     71 
     72         PUCHAR subAuthorityCount = GetSidSubAuthorityCount(sid);
     73         THROW_HR_IF(E_UNEXPECTED, *subAuthorityCount != 1);
     74 
     75         PDWORD subAuthority = GetSidSubAuthority(sid, 0);
     76 
     77         switch (*subAuthority)
     78         {
     79         case SECURITY_MANDATORY_UNTRUSTED_RID: return IntegrityLevel::Untrusted;
     80         case SECURITY_MANDATORY_LOW_RID: return IntegrityLevel::Low;
     81         case SECURITY_MANDATORY_MEDIUM_RID: return IntegrityLevel::Medium;
     82         case SECURITY_MANDATORY_HIGH_RID: return IntegrityLevel::High;
     83         case SECURITY_MANDATORY_SYSTEM_RID: return IntegrityLevel::System;
     84         case SECURITY_MANDATORY_PROTECTED_PROCESS_RID: return IntegrityLevel::ProtectedProcess;
     85         }
     86 
     87         THROW_HR(E_UNEXPECTED);
     88     }
     89 
     90     bool IsCOMCallerSameUserAndIntegrityLevel()
     91     {
     92         auto serverUser = wil::get_token_information<TOKEN_USER>();
     93         IntegrityLevel serverIntegrityLevel = GetEffectiveIntegrityLevel();
     94 
     95         auto impersonation = ImpersonateCOMorRPCCaller::BeginImpersonation();
     96 
     97         auto callingUser = wil::get_token_information<TOKEN_USER>();
     98         IntegrityLevel callingIntegrityLevel = GetEffectiveIntegrityLevel();
     99 
    100         if (!EqualSid(serverUser->User.Sid, callingUser->User.Sid))
    101         {
    102             AICLI_LOG(Core, Crit, << "Attempt to access by another user: " << ToString(callingUser->User.Sid));
    103             return false;
    104         }
    105 
    106         if (ToIntegral(callingIntegrityLevel) < ToIntegral(serverIntegrityLevel))
    107         {
    108             AICLI_LOG(Core, Crit, << "Attempt to access by a lower integrity process: " << callingIntegrityLevel << " < " << serverIntegrityLevel);
    109             return false;
    110         }
    111 
    112         return true;
    113     }
    114 
    115     bool IsCOMCallerIntegrityLevelAtLeast(IntegrityLevel minimumLevel)
    116     {
    117         auto impersonation = ImpersonateCOMorRPCCaller::BeginImpersonation();
    118         return IsCurrentIntegrityLevelAtLeast(minimumLevel);
    119     }
    120 
    121     bool IsCurrentIntegrityLevelAtLeast(IntegrityLevel minimumLevel)
    122     {
    123         IntegrityLevel callingIntegrityLevel = GetEffectiveIntegrityLevel();
    124 
    125         if (ToIntegral(callingIntegrityLevel) < ToIntegral(minimumLevel))
    126         {
    127             AICLI_LOG(Core, Crit, << "Attempt to access by a lower integrity process than required: " << callingIntegrityLevel << " < " << minimumLevel);
    128             return false;
    129         }
    130 
    131         return true;
    132     }
    133 
    134     std::string ToString(PSID sid)
    135     {
    136         wil::unique_hlocal_ansistring result;
    137         THROW_IF_WIN32_BOOL_FALSE(ConvertSidToStringSidA(sid, &result));
    138         return result.get();
    139     }
    140 }