winget-cli

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

WinMain.cpp (9416B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #define NOMINMAX
      4 #pragma warning( push )
      5 #pragma warning ( disable : 6001 6388 6553)
      6 #include <wil/resource.h>
      7 #include <wil/com.h>
      8 #pragma warning( pop )
      9 #include <objidl.h>
     10 #include <shellapi.h>
     11 #include <sddl.h>
     12 #include <WindowsPackageManager.h>
     13 #include "WinGetServer.h"
     14 #include "Utils.h"
     15 
     16 #include <memory>
     17 #include <string>
     18 #include <string_view>
     19 #include <vector>
     20 
     21 // Holds the wwinmain open until COM tells us there are no more server connections
     22 wil::unique_event _comServerExitEvent;
     23 
     24 // Routine Description:
     25 // - Called back when COM says there is nothing left for our server to do and we can tear down.
     26 static void _releaseNotifier() noexcept
     27 {
     28     _comServerExitEvent.SetEvent();
     29 }
     30 
     31 HRESULT WindowsPackageManagerServerInitializeRPCServer()
     32 {
     33     std::string userSID = GetUserSID();
     34     std::string endpoint = "\\pipe\\WinGetServerManualActivation_" + userSID;
     35     RPC_STATUS status = RpcServerUseProtseqEpA(GetUCharString("ncacn_np"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, GetUCharString(endpoint), nullptr);
     36     RETURN_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK);
     37 
     38     // The goal of this security descriptor is to restrict RPC server access only to the user in admin mode. 
     39     // (ML;;NW;;;HI) specifies a high mandatory integrity level (requires admin).
     40     // (A;;GA;;;UserSID) specifies access only for the user with the user SID (i.e. self).
     41     wil::unique_hlocal_security_descriptor securityDescriptor;
     42     std::string securityDescriptorString = "S:(ML;;NW;;;HI)D:(A;;GA;;;" + userSID + ")";
     43     RETURN_LAST_ERROR_IF(!ConvertStringSecurityDescriptorToSecurityDescriptorA(securityDescriptorString.c_str(), SDDL_REVISION_1, &securityDescriptor, nullptr));
     44 
     45     status = RpcServerRegisterIf3(WinGetServerManualActivation_v1_0_s_ifspec, nullptr, nullptr, RPC_IF_ALLOW_LOCAL_ONLY | RPC_IF_AUTOLISTEN, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0, nullptr, securityDescriptor.get());
     46     RETURN_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK);
     47 
     48     return S_OK;
     49 }
     50 
     51 _Must_inspect_result_
     52 _Ret_maybenull_ _Post_writable_byte_size_(size)
     53 void* __RPC_USER MIDL_user_allocate(_In_ size_t size)
     54 {
     55     return malloc(size);
     56 }
     57 
     58 void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void* ptr)
     59 {
     60     if (ptr)
     61     {
     62         free(ptr);
     63     }
     64 }
     65 
     66 extern "C" HRESULT CreateInstance(
     67     /* [in] */ GUID clsid,
     68     /* [in] */ GUID iid,
     69     /* [in] */ UINT32,
     70     /* [ref][out] */ UINT32 * pcbBuffer,
     71     /* [size_is][size_is][ref][out] */ BYTE * *ppBuffer)
     72 {
     73     RETURN_HR_IF_NULL(E_POINTER, pcbBuffer);
     74     RETURN_HR_IF_NULL(E_POINTER, ppBuffer);
     75 
     76     wil::com_ptr<IStream> stream;
     77     RETURN_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream));
     78 
     79     wil::com_ptr<IUnknown> instance;
     80     RETURN_IF_FAILED(WindowsPackageManagerServerCreateInstance(clsid, iid, reinterpret_cast<void**>(&instance)));
     81 
     82     RETURN_IF_FAILED(CoMarshalInterface(stream.get(), iid, instance.get(), MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL));
     83 
     84     ULARGE_INTEGER streamSize{};
     85     RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_CUR, &streamSize));
     86     RETURN_HR_IF(E_NOT_SUFFICIENT_BUFFER, streamSize.QuadPart > std::numeric_limits<UINT32>::max());
     87 
     88     UINT32 bufferSize = static_cast<UINT32>(streamSize.QuadPart);
     89 
     90     struct DeleteWithMidlFree { void operator()(void* m) { MIDL_user_free(m); } };
     91     std::unique_ptr<BYTE, DeleteWithMidlFree> buffer{ reinterpret_cast<BYTE*>(MIDL_user_allocate(bufferSize)) };
     92 
     93     RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr));
     94     ULONG bytesRead = 0;
     95     RETURN_IF_FAILED(stream->Read(buffer.get(), bufferSize, &bytesRead));
     96     RETURN_HR_IF(E_UNEXPECTED, bytesRead != bufferSize);
     97 
     98     *pcbBuffer = bufferSize;
     99     *ppBuffer = buffer.release();
    100 
    101     return S_OK;
    102 }
    103 
    104 HRESULT InitializeComSecurity()
    105 {
    106     wil::unique_hlocal_security_descriptor securityDescriptor;
    107     // Allow Self, System, Built-in Admin and App Container access. 3 is COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL
    108     std::string securityDescriptorString = "O:SYG:SYD:(A;;3;;;PS)(A;;3;;;SY)(A;;3;;;BA)(A;;3;;;AC)";
    109     RETURN_LAST_ERROR_IF(!ConvertStringSecurityDescriptorToSecurityDescriptorA(securityDescriptorString.c_str(), SDDL_REVISION_1, &securityDescriptor, nullptr));
    110 
    111     // Make absolute security descriptor as CoInitializeSecurity required
    112     SECURITY_DESCRIPTOR absoluteSecurityDescriptor;
    113     DWORD securityDescriptorSize = sizeof(SECURITY_DESCRIPTOR);
    114 
    115     DWORD daclSize = 0;
    116     DWORD saclSize = 0;
    117     DWORD ownerSize = 0;
    118     DWORD groupSize = 0;
    119 
    120     // Get required size
    121     BOOL result = MakeAbsoluteSD(securityDescriptor.get(), &absoluteSecurityDescriptor, &securityDescriptorSize, nullptr, &daclSize, nullptr, &saclSize, nullptr, &ownerSize, nullptr, &groupSize);
    122     RETURN_HR_IF_MSG(E_FAIL, result || GetLastError() != ERROR_INSUFFICIENT_BUFFER, "MakeAbsoluteSD failed to return buffer sizes");
    123 
    124     std::vector<BYTE> dacl(daclSize);
    125     std::vector<BYTE> sacl(saclSize);
    126     std::vector<BYTE> owner(ownerSize);
    127     std::vector<BYTE> group(groupSize);
    128 
    129     RETURN_LAST_ERROR_IF(!MakeAbsoluteSD(securityDescriptor.get(), &absoluteSecurityDescriptor, &securityDescriptorSize, (PACL)dacl.data(), &daclSize, (PACL)sacl.data(), &saclSize, (PACL)owner.data(), &ownerSize, (PACL)group.data(), &groupSize));
    130 
    131     // Initialize com security
    132     RETURN_IF_FAILED(CoInitializeSecurity(
    133         &absoluteSecurityDescriptor, // Security descriptor
    134         -1, // Authentication services count. -1 is let com choose.
    135         nullptr, // Authentication services array
    136         nullptr, // Reserved
    137         RPC_C_AUTHN_LEVEL_DEFAULT, // Authentication level.
    138         RPC_C_IMP_LEVEL_IDENTIFY, // Impersonation level. Identify client.
    139         nullptr, // Authentication list
    140         EOAC_NONE, // Additional capabilities
    141         nullptr // Reserved
    142     ));
    143 
    144     return S_OK;
    145 }
    146 
    147 int __stdcall wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR cmdLine, _In_ int)
    148 {
    149     wil::SetResultLoggingCallback(&WindowsPackageManagerServerWilResultLoggingCallback);
    150 
    151     RETURN_IF_FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED));
    152 
    153     // Enable fast rundown of objects so that the server exits faster when clients go away.
    154     {
    155         wil::com_ptr<IGlobalOptions> globalOptions;
    156         RETURN_IF_FAILED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&globalOptions)));
    157         RETURN_IF_FAILED(globalOptions->Set(COMGLB_RO_SETTINGS, COMGLB_FAST_RUNDOWN));
    158         RETURN_IF_FAILED(globalOptions->Set(COMGLB_UNMARSHALING_POLICY, COMGLB_UNMARSHALING_POLICY_STRONG));
    159         RETURN_IF_FAILED(globalOptions->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE_ANY));
    160     }
    161 
    162     // Command line parsing
    163     int argc = 0;
    164     LPWSTR* argv = CommandLineToArgvW(cmdLine, &argc);
    165     RETURN_LAST_ERROR_IF(!argv);
    166 
    167     bool manualActivation = false;
    168 
    169     // If command line gets more complicated, consider more complex parsing
    170     if (argc == 1 && std::wstring_view{ L"--manualActivation" } == argv[0])
    171     {
    172         manualActivation = true;
    173     }
    174 
    175     // For packaged com activation, initialize com security.
    176     // For manual activation, leave as default. We'll not register objects for manual activation.
    177     if (!manualActivation)
    178     {
    179         // This must be called after IGlobalOptions (fast rundown setting cannot be changed after CoInitializeSecurity)
    180         // This must be called before WindowsPackageManagerServerInitialize (when setting the logs
    181         // to Windows.Storage folders, automatic CoInitializeSecurity is triggered)
    182         RETURN_IF_FAILED(InitializeComSecurity());
    183     }
    184 
    185     RETURN_IF_FAILED(WindowsPackageManagerServerInitialize());
    186 
    187     _comServerExitEvent.create();
    188     RETURN_IF_FAILED(WindowsPackageManagerServerModuleCreate(&_releaseNotifier));
    189     try
    190     {
    191         // Manual reset event to notify the client that the server is available.
    192         wil::unique_event manualResetEvent;
    193 
    194         if (manualActivation)
    195         {
    196             // For manual activation, do not register com objects
    197             // so that only RPC channel can be used.
    198             HANDLE hMutex = NULL;
    199             hMutex = CreateMutex(NULL, FALSE, TEXT("WinGetServerMutex"));
    200             RETURN_LAST_ERROR_IF_NULL(hMutex);
    201 
    202             DWORD waitResult = WaitForSingleObject(hMutex, 0);
    203             if (waitResult != WAIT_OBJECT_0 && waitResult != WAIT_ABANDONED)
    204             {
    205                 return HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING);
    206             }
    207 
    208             RETURN_IF_FAILED(WindowsPackageManagerServerInitializeRPCServer());
    209 
    210             manualResetEvent = CreateOrOpenServerStartEvent();
    211             manualResetEvent.SetEvent();
    212         }
    213         else
    214         {
    215             // Register all the CoCreatableClassWrlCreatorMapInclude classes
    216             RETURN_IF_FAILED(WindowsPackageManagerServerModuleRegister());
    217         }
    218 
    219         _comServerExitEvent.wait();
    220 
    221         if (manualResetEvent)
    222         {
    223             manualResetEvent.reset();
    224         }
    225 
    226         if (!manualActivation)
    227         {
    228             RETURN_IF_FAILED(WindowsPackageManagerServerModuleUnregister());
    229         }
    230     }
    231     CATCH_RETURN()
    232 
    233     return 0;
    234 }