From 620abad4f9c9aed64502bc59e8a89ec88bbb1842 Mon Sep 17 00:00:00 2001 From: Anand Arumugam Date: Sat, 13 Dec 2025 10:59:57 -0800 Subject: [PATCH] Win32 example to demonstrate how to do IPC using named pipe The example show cases how to IPC between a full trust parent process and a sandboxed (AppContainer) child process using named pipes. --- Samples/MSIXSamples/SandboxIPC/IPCDemo.sln | 100 ++++ Samples/MSIXSamples/SandboxIPC/README.md | 127 +++++ Samples/MSIXSamples/SandboxIPC/demo_child.cpp | 194 ++++++++ .../MSIXSamples/SandboxIPC/demo_child.vcxproj | 100 ++++ Samples/MSIXSamples/SandboxIPC/demo_host.cpp | 449 ++++++++++++++++++ .../MSIXSamples/SandboxIPC/demo_host.vcxproj | 100 ++++ .../WinhttpProxySample/BasicProxyMain.cpp | 4 +- .../cpp/FSRM_Get_Enum_Properties.cpp | 2 +- 8 files changed, 1073 insertions(+), 3 deletions(-) create mode 100644 Samples/MSIXSamples/SandboxIPC/IPCDemo.sln create mode 100644 Samples/MSIXSamples/SandboxIPC/README.md create mode 100644 Samples/MSIXSamples/SandboxIPC/demo_child.cpp create mode 100644 Samples/MSIXSamples/SandboxIPC/demo_child.vcxproj create mode 100644 Samples/MSIXSamples/SandboxIPC/demo_host.cpp create mode 100644 Samples/MSIXSamples/SandboxIPC/demo_host.vcxproj diff --git a/Samples/MSIXSamples/SandboxIPC/IPCDemo.sln b/Samples/MSIXSamples/SandboxIPC/IPCDemo.sln new file mode 100644 index 000000000..bbff78b52 --- /dev/null +++ b/Samples/MSIXSamples/SandboxIPC/IPCDemo.sln @@ -0,0 +1,100 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demo_host", "demo_host.vcxproj", "{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demo_child", "demo_child.vcxproj", "{B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|Any CPU.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|Any CPU.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|ARM.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|ARM.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|ARM64.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|ARM64.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|x64.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|x64.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|x86.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|x86.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|Any CPU.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|Any CPU.Build.0 = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|ARM.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|ARM.Build.0 = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|ARM64.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|ARM64.Build.0 = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|x64.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|x64.Build.0 = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|x86.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|x86.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|Any CPU.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|Any CPU.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|ARM.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|ARM.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|ARM64.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|ARM64.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|x64.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|x64.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|x86.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|x86.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|Any CPU.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|Any CPU.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|ARM.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|ARM.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|ARM64.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|ARM64.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|x64.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|x64.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|x86.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|x86.Build.0 = Release|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM.ActiveCfg = Debug|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM.Build.0 = Debug|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM.Deploy.0 = Debug|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM64.Build.0 = Debug|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x64.ActiveCfg = Debug|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x64.Build.0 = Debug|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x64.Deploy.0 = Debug|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x86.ActiveCfg = Debug|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x86.Build.0 = Debug|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x86.Deploy.0 = Debug|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|Any CPU.Build.0 = Release|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|Any CPU.Deploy.0 = Release|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM.ActiveCfg = Release|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM.Build.0 = Release|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM.Deploy.0 = Release|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM64.ActiveCfg = Release|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM64.Build.0 = Release|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM64.Deploy.0 = Release|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x64.ActiveCfg = Release|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x64.Build.0 = Release|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x64.Deploy.0 = Release|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x86.ActiveCfg = Release|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x86.Build.0 = Release|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {12345678-1234-1234-1234-123456789012} + EndGlobalSection +EndGlobal diff --git a/Samples/MSIXSamples/SandboxIPC/README.md b/Samples/MSIXSamples/SandboxIPC/README.md new file mode 100644 index 000000000..266366652 --- /dev/null +++ b/Samples/MSIXSamples/SandboxIPC/README.md @@ -0,0 +1,127 @@ +# IPC Demo - Parent-Child Process Communication + +This project demonstrates interprocess communication (IPC) between a full trust parent process and a sandboxed (AppContainer) child process using named pipes. + +## Architecture + +- **demo_parent.exe**: Full trust console application that: + - Spawns the child process using the app execution alias (`demo_child_ac.exe`) + - Creates a named pipe with proper security attributes to allow AppContainer access + - Sends messages to the child and receives echoed responses + - Manages the child process lifecycle + +- **demo_child.exe**: Sandboxed (AppContainer) console application that: + - Runs in a restricted security context + - Connects to the named pipe created by the parent + - Receives messages and echoes them back + - Exits gracefully when receiving the EXIT command + +## Key Features + +### Security Attributes for Named Pipe + +The parent process creates the named pipe with a security descriptor that allows AppContainer processes to access it: + +```cpp +// SDDL: Grants Generic All to World Domain and Generic Read/Write to ALL APPLICATION PACKAGES +LPCWSTR sddl = L"D:(A;;GA;;;WD)(A;;GRGW;;;AC)"; +``` + +This is critical for allowing the sandboxed child process to communicate with the full trust parent. + +### App Execution Alias + +The manifest uses the `uap5:AppExecutionAlias` feature to create aliases: +- `demo_parent.exe` - Full trust parent +- `demo_child_ac.exe` - Sandboxed child (alias for `demo_child.exe`) + +The parent spawns the child using `demo_child_ac.exe`, which automatically runs it in the AppContainer sandbox. + +## Building + +1. Open `IPCDemo.sln` in Visual Studio 2022 or later +2. Build the solution (both Debug and Release configurations supported) +3. Executables will be output to `bin\Debug\` or `bin\Release\` + +## Creating the APPX Package + +To deploy this as a packaged application: + +1. Create a folder structure: + ``` + Package\ + ├── demo_parent.exe + ├── demo_child.exe + ├── AppxManifest.xml + └── Images\ + ├── StoreLogo.png + └── AppList.png + ``` + +2. Copy the built executables to the Package folder + +3. Create placeholder images (or use real ones): + - StoreLogo.png (150x150) + - AppList.png (44x44) + +4. Use `MakeAppx.exe` to create the package: + ```powershell + MakeAppx.exe pack /d Package /p IPCDemo.appx + ``` + +5. Sign the package (for testing, use a test certificate): + ```powershell + SignTool.exe sign /fd SHA256 /a /f TestCert.pfx IPCDemo.appx + ``` + +## Running + +### From Visual Studio (Debug) + +Simply run the `demo_parent` project. It will automatically launch the child process. + +### From Command Line (Packaged) + +1. Install the package: `Add-AppxPackage .\IPCDemo.appx` +2. Run: `demo_parent.exe` + +## Communication Flow + +1. Parent starts and creates security descriptor +2. Parent launches child using `demo_child_ac.exe` alias +3. Parent creates named pipe with AppContainer-accessible security +4. Parent waits for child to connect +5. Child waits for pipe availability and connects +6. Parent sends "Hello from parent!" → Child echoes back +7. Parent sends "This is message number 2" → Child echoes back +8. Parent sends "EXIT" command +9. Child closes pipe and exits +10. Parent waits for child exit and closes + +## Important Notes + +- The child process runs in an AppContainer, which has restricted access to system resources +- The named pipe security descriptor must explicitly grant access to AppContainer processes (SID: S-1-15-2-1) +- The app execution alias mechanism ensures the child runs in the correct security context +- Both processes are console applications for easy debugging and demonstration + +## Requirements + +- Windows 10 version 1809 (17763) or later +- Visual Studio 2022 with C++ development tools +- Windows SDK 10.0 or later + +## Troubleshooting + +If the child fails to connect: +- Ensure the security descriptor on the pipe includes AppContainer access (`AC` in SDDL) +- Verify the child is being launched with the `_ac.exe` alias +- Check that the package is properly installed and the manifest is correct +- Look for error codes in the console output + +## References + +- [AppContainer Isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation) +- [Named Pipes](https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes) +- [Security Descriptor String Format (SDDL)](https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format) +- [App Execution Alias](https://docs.microsoft.com/en-us/windows/uwp/launch-resume/execute-in-app-context) diff --git a/Samples/MSIXSamples/SandboxIPC/demo_child.cpp b/Samples/MSIXSamples/SandboxIPC/demo_child.cpp new file mode 100644 index 000000000..ff4f5adb4 --- /dev/null +++ b/Samples/MSIXSamples/SandboxIPC/demo_child.cpp @@ -0,0 +1,194 @@ +#include +#include +#include +#include + +#define PIPE_NAME L"\\\\.\\pipe\\IPCDemoPipe" +#define BUFFER_SIZE 512 + +// Function to get and display the current process integrity level and +// AppContainer status +void DisplayProcessSecurityInfo() { + HANDLE hToken = nullptr; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { + printf("[Child] Failed to open process token. Error: %lu\n", + GetLastError()); + return; + } + + // Get integrity level + DWORD dwLengthNeeded = 0; + GetTokenInformation(hToken, TokenIntegrityLevel, nullptr, 0, &dwLengthNeeded); + + TOKEN_MANDATORY_LABEL *pTIL = + (TOKEN_MANDATORY_LABEL *)LocalAlloc(0, dwLengthNeeded); + if (pTIL) { + if (GetTokenInformation(hToken, TokenIntegrityLevel, pTIL, dwLengthNeeded, + &dwLengthNeeded)) { + DWORD dwIntegrityLevel = *GetSidSubAuthority( + pTIL->Label.Sid, + (DWORD)(UCHAR)(*GetSidSubAuthorityCount(pTIL->Label.Sid) - 1)); + + const char *integrityLevelStr = "Unknown"; + if (dwIntegrityLevel < SECURITY_MANDATORY_LOW_RID) + integrityLevelStr = "Untrusted"; + else if (dwIntegrityLevel < SECURITY_MANDATORY_MEDIUM_RID) + integrityLevelStr = "Low"; + else if (dwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID) + integrityLevelStr = "Medium"; + else if (dwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID) + integrityLevelStr = "High/System"; + + printf("[Child] *** Process Integrity Level: %s (0x%X) ***\n", + integrityLevelStr, dwIntegrityLevel); + + // Convert SID to string for display + LPSTR szIntegritySid = nullptr; + if (ConvertSidToStringSidA(pTIL->Label.Sid, &szIntegritySid)) { + printf("[Child] *** Integrity SID: %s ***\n", szIntegritySid); + LocalFree(szIntegritySid); + } + } + LocalFree(pTIL); + } + + // Check if running in AppContainer + DWORD dwIsAppContainer = 0; + dwLengthNeeded = sizeof(DWORD); + if (GetTokenInformation(hToken, TokenIsAppContainer, &dwIsAppContainer, + sizeof(DWORD), &dwLengthNeeded)) { + printf("[Child] *** Running in AppContainer: %s ***\n", + dwIsAppContainer ? "YES" : "NO"); + } + + // Get AppContainer SID if applicable + if (dwIsAppContainer) { + dwLengthNeeded = 0; + GetTokenInformation(hToken, TokenAppContainerSid, nullptr, 0, + &dwLengthNeeded); + + TOKEN_APPCONTAINER_INFORMATION *pAppContainerInfo = + (TOKEN_APPCONTAINER_INFORMATION *)LocalAlloc(0, dwLengthNeeded); + if (pAppContainerInfo) { + if (GetTokenInformation(hToken, TokenAppContainerSid, pAppContainerInfo, + dwLengthNeeded, &dwLengthNeeded)) { + LPWSTR szAppContainerSid = nullptr; + if (ConvertSidToStringSidW(pAppContainerInfo->TokenAppContainer, + &szAppContainerSid)) { + printf("[Child] *** AppContainer SID: %ls ***\n", szAppContainerSid); + LocalFree(szAppContainerSid); + } + } + LocalFree(pAppContainerInfo); + } + } + + CloseHandle(hToken); +} + +bool SendMessage(HANDLE hPipe, const char *message) { + DWORD bytesWritten; + DWORD messageLen = (DWORD)strlen(message) + 1; + + printf("[Child] Sending: %s\n", message); + + if (!WriteFile(hPipe, message, messageLen, &bytesWritten, nullptr)) { + printf("[Child] Failed to write to pipe. Error: %lu\n", GetLastError()); + return false; + } + + return true; +} + +bool ReceiveMessage(HANDLE hPipe, char *buffer, DWORD bufferSize) { + DWORD bytesRead; + + if (!ReadFile(hPipe, buffer, bufferSize, &bytesRead, nullptr)) { + printf("[Child] Failed to read from pipe. Error: %lu\n", GetLastError()); + return false; + } + + printf("[Child] Received: %s\n", buffer); + return true; +} + +int main() { + printf("=== IPC Demo - Child Process (Sandboxed) ===\n"); + printf("[Child] Starting...\n"); + + // Display security information + printf("\n[Child] === Security Context Information ===\n"); + DisplayProcessSecurityInfo(); + printf("[Child] ==========================================\n\n"); + + // Wait for pipe to be available + printf("[Child] Waiting for pipe to become available...\n"); + + int retries = 0; + while (!WaitNamedPipeW(PIPE_NAME, 5000)) { + printf("[Child] Pipe not available yet, retrying... (attempt %d)\n", + ++retries); + if (retries > 10) { + printf("[Child] Timeout waiting for pipe\n"); + return 1; + } + } + + printf("[Child] Pipe is available, connecting...\n"); + + // Open the named pipe + HANDLE hPipe = ::CreateFileW(PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, + nullptr, OPEN_EXISTING, 0, nullptr); + + if (hPipe == INVALID_HANDLE_VALUE) { + printf("[Child] Failed to open pipe. Error: %lu\n", GetLastError()); + return 1; + } + + printf("[Child] Connected to pipe\n"); + + // Set pipe to message mode + DWORD mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(hPipe, &mode, nullptr, nullptr)) { + printf("[Child] Failed to set pipe mode. Error: %lu\n", GetLastError()); + CloseHandle(hPipe); + return 1; + } + + char buffer[BUFFER_SIZE]; + + // Message loop + while (true) { + // Receive message from parent + if (!ReceiveMessage(hPipe, buffer, BUFFER_SIZE)) { + break; + } + + // Check for exit command + if (strcmp(buffer, "EXIT") == 0) { + printf("[Child] Received exit command\n"); + break; + } + + // Echo the message back + char response[BUFFER_SIZE]; + snprintf(response, BUFFER_SIZE, "Echo: %s", buffer); + + if (!SendMessage(hPipe, response)) { + break; + } + } + + // Close pipe + printf("[Child] Closing pipe\n"); + CloseHandle(hPipe); + + printf("[Child] Child process exiting\n"); + + // Wait for user input before closing the console + printf("\n[Child] *** Press Enter to close this window... ***\n"); + getchar(); + + return 0; +} diff --git a/Samples/MSIXSamples/SandboxIPC/demo_child.vcxproj b/Samples/MSIXSamples/SandboxIPC/demo_child.vcxproj new file mode 100644 index 000000000..9eeb7c518 --- /dev/null +++ b/Samples/MSIXSamples/SandboxIPC/demo_child.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E} + Win32Proj + DemoChild + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + demo_child + + + false + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + demo_child + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + kernel32.lib;user32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + kernel32.lib;user32.lib;%(AdditionalDependencies) + + + + + + + + + diff --git a/Samples/MSIXSamples/SandboxIPC/demo_host.cpp b/Samples/MSIXSamples/SandboxIPC/demo_host.cpp new file mode 100644 index 000000000..f32ad2f90 --- /dev/null +++ b/Samples/MSIXSamples/SandboxIPC/demo_host.cpp @@ -0,0 +1,449 @@ +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "userenv.lib") + +#define PIPE_NAME L"\\\\.\\pipe\\IPCDemoPipe" +#define BUFFER_SIZE 512 +#define APPCONTAINER_NAME L"SandboxIpcDemoApp" + +// Helper to get Log On SID from current process token +bool GetLogonSid(PSID &logon_sid) { + HANDLE token_handle; + if (0 == + ::OpenProcessToken(::GetCurrentProcess(), TOKEN_READ, &token_handle)) { + auto gle = ::GetLastError(); + printf("OpenProcessToken failed. Error: %lu\n", gle); + return false; + } + + DWORD token_info_length = 0; + ::GetTokenInformation(token_handle, TokenGroups, nullptr, 0, + &token_info_length); + + void *token_info_buffer = + HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, token_info_length); + + if (!token_info_buffer) { + printf("HeapAlloc failed. Error: %lu\n", GetLastError()); + return false; + } + + if (!::GetTokenInformation(token_handle, TokenGroups, token_info_buffer, + token_info_length, &token_info_length)) { + printf("GetTokenInformation failed. Error: %lu\n", GetLastError()); + HeapFree(GetProcessHeap(), 0, token_info_buffer); + return false; + } + + bool result = false; + TOKEN_GROUPS *token_groups = static_cast(token_info_buffer); + for (DWORD i = 0; i < token_groups->GroupCount; i++) { + auto token = token_groups->Groups[i]; + if ((token.Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) { + logon_sid = token.Sid; + result = true; + } + } + + HeapFree(GetProcessHeap(), 0, token_info_buffer); + return result; +} + +// Function to get and display the current process integrity level +void DisplayProcessSecurityInfo() { + HANDLE hToken = nullptr; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { + printf("[Host] Failed to open process token. Error: %lu\n", GetLastError()); + return; + } + + // Get integrity level + DWORD dwLengthNeeded = 0; + GetTokenInformation(hToken, TokenIntegrityLevel, nullptr, 0, &dwLengthNeeded); + + TOKEN_MANDATORY_LABEL *pTIL = + (TOKEN_MANDATORY_LABEL *)LocalAlloc(0, dwLengthNeeded); + if (pTIL) { + if (GetTokenInformation(hToken, TokenIntegrityLevel, pTIL, dwLengthNeeded, + &dwLengthNeeded)) { + DWORD dwIntegrityLevel = *GetSidSubAuthority( + pTIL->Label.Sid, + (DWORD)(UCHAR)(*GetSidSubAuthorityCount(pTIL->Label.Sid) - 1)); + + const char *integrityLevelStr = "Unknown"; + if (dwIntegrityLevel < SECURITY_MANDATORY_LOW_RID) + integrityLevelStr = "Untrusted"; + else if (dwIntegrityLevel < SECURITY_MANDATORY_MEDIUM_RID) + integrityLevelStr = "Low"; + else if (dwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID) + integrityLevelStr = "Medium"; + else if (dwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID) + integrityLevelStr = "High/System"; + + printf("[Parent] *** Process Integrity Level: %s (0x%X) ***\n", + integrityLevelStr, dwIntegrityLevel); + + // Convert SID to string for display + LPWSTR szIntegritySid = nullptr; + if (ConvertSidToStringSidW(pTIL->Label.Sid, &szIntegritySid)) { + printf("[Parent] *** Integrity SID: %ls ***\n", szIntegritySid); + LocalFree(szIntegritySid); + } + } + LocalFree(pTIL); + } + + // Check if running in AppContainer + DWORD dwIsAppContainer = 0; + dwLengthNeeded = sizeof(DWORD); + if (GetTokenInformation(hToken, TokenIsAppContainer, &dwIsAppContainer, + sizeof(DWORD), &dwLengthNeeded)) { + printf("[Parent] *** Running in AppContainer: %s ***\n", + dwIsAppContainer ? "YES" : "NO"); + } + + CloseHandle(hToken); +} + +bool GetSecurityAttributes(SECURITY_ATTRIBUTES &sa, SECURITY_DESCRIPTOR &sd, + const PSID &app_container_sid) { + // Create a new security descriptor + if (0 == ::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { + auto gle = ::GetLastError(); + printf("InitializeSecurityDescriptor failed. Error: %lu\n", gle); + return false; + } + + PSID logon_sid; + if (!GetLogonSid(logon_sid)) { + printf("Failed to get logon SID\n"); + return false; + } + + LPSTR logon_sid_string = nullptr; + if (::ConvertSidToStringSidA(logon_sid, &logon_sid_string)) { + printf("Logon SID: %s\n", logon_sid_string); + LocalFree(logon_sid_string); // Free memory allocated by + } + + // Create a new ACL + PACL acl = nullptr; + constexpr unsigned int kMaxExplicitAccessEntries = 2; + EXPLICIT_ACCESSW ea_list[kMaxExplicitAccessEntries] = {}; + + // first we give access to the "Everyone" SID + ea_list[0].grfAccessPermissions = FILE_ALL_ACCESS; + ea_list[0].grfAccessMode = GRANT_ACCESS; + ea_list[0].grfInheritance = 0; + ea_list[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea_list[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + ea_list[0].Trustee.pMultipleTrustee = nullptr; + ea_list[0].Trustee.ptstrName = static_cast(logon_sid); + + // here we explicitly give access to the AppContainer SID + ea_list[1].grfAccessPermissions = FILE_ALL_ACCESS; + ea_list[1].grfAccessMode = GRANT_ACCESS; + ea_list[1].grfInheritance = 0; + ea_list[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea_list[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + ea_list[1].Trustee.pMultipleTrustee = nullptr; + ea_list[1].Trustee.ptstrName = static_cast(app_container_sid); + + auto result = + ::SetEntriesInAclW(kMaxExplicitAccessEntries, ea_list, nullptr, &acl); + if (result != ERROR_SUCCESS) { + printf("SetEntriesInAclW failed. Error: %lu\n", result); + return false; + } + + // Set the DACL in the security descriptor + if (!::SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE)) { + printf("SetSecurityDescriptorDacl failed. Error: %lu\n", GetLastError()); + return false; + } + sa.lpSecurityDescriptor = &sd; + return true; +} + +bool SendMessage(HANDLE hPipe, const char *message); +bool ReceiveMessage(HANDLE hPipe, char *buffer, DWORD bufferSize); + +int main() { + printf("=== IPC Demo - Host Process ===\n"); + printf("[Host] Starting...\n"); + + // Display security information + printf("\n[Host] === Security Context Information ===\n"); + DisplayProcessSecurityInfo(); + printf("[Host] ==========================================\n\n"); + + // Create or get AppContainer profile for the child process + PSID pAppContainerSid = nullptr; + HRESULT hr = CreateAppContainerProfile( + APPCONTAINER_NAME, L"IPC Demo Child AppContainer", + L"Sandboxed child process for IPC demonstration", + nullptr, // No capabilities for now + 0, &pAppContainerSid); + + if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) { + printf("[Parent] Failed to create AppContainer profile. HRESULT: 0x%08X\n", + hr); + return 1; + } + + // If AppContainerProfile already exists, get the SID + if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) { + hr = DeriveAppContainerSidFromAppContainerName(APPCONTAINER_NAME, + &pAppContainerSid); + if (FAILED(hr)) { + printf("[Parent] Failed to get AppContainer SID. HRESULT: 0x%08X\n", hr); + return 1; + } + } + + // Use the the AppContainer SID to create a security attributes for the Named + // Pipe + SECURITY_DESCRIPTOR sd = {0}; + SECURITY_ATTRIBUTES sa = {0}; + sa.bInheritHandle = FALSE; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + + if (!GetSecurityAttributes(sa, sd, pAppContainerSid)) { + printf("[Host] Failed to create security attributes\n"); + return 1; + } + + // Create named pipe BEFORE launching child process + printf("[Host] Creating named pipe: %ls\n", PIPE_NAME); + + HANDLE hPipe = ::CreateNamedPipeW(PIPE_NAME, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | + PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, + 1, BUFFER_SIZE, BUFFER_SIZE, 0, &sa); + + if (hPipe == INVALID_HANDLE_VALUE) { + printf("[Parent] Failed to create named pipe. Error: %lu\n", + GetLastError()); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + printf("[Parent] Named pipe created successfully\n"); + + // Set up security capabilities for the AppContainer + SECURITY_CAPABILITIES securityCapabilities = {0}; + securityCapabilities.AppContainerSid = pAppContainerSid; + securityCapabilities.Capabilities = nullptr; + securityCapabilities.CapabilityCount = 0; + securityCapabilities.Reserved = 0; + + // Set up process attribute list + SIZE_T attributeListSize = 0; + InitializeProcThreadAttributeList(nullptr, 1, 0, &attributeListSize); + + LPPROC_THREAD_ATTRIBUTE_LIST pAttributeList = + (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, + attributeListSize); + + if (!pAttributeList) { + printf("[Parent] Failed to allocate attribute list\n"); + FreeSid(pAppContainerSid); + CloseHandle(hPipe); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + if (!InitializeProcThreadAttributeList(pAttributeList, 1, 0, + &attributeListSize)) { + printf("[Parent] Failed to initialize attribute list. Error: %lu\n", + GetLastError()); + HeapFree(GetProcessHeap(), 0, pAttributeList); + FreeSid(pAppContainerSid); + CloseHandle(hPipe); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + if (!UpdateProcThreadAttribute( + pAttributeList, 0, PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES, + &securityCapabilities, sizeof(securityCapabilities), nullptr, + nullptr)) { + printf("[Parent] Failed to update proc thread attribute. Error: %lu\n", + GetLastError()); + DeleteProcThreadAttributeList(pAttributeList); + HeapFree(GetProcessHeap(), 0, pAttributeList); + FreeSid(pAppContainerSid); + CloseHandle(hPipe); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + // NOW launch the child process + STARTUPINFOW si = {0}; + PROCESS_INFORMATION pi = {0}; + si.cb = sizeof(si); + + // Use STARTUPINFOEX to pass the attribute list + STARTUPINFOEXW siex = {0}; + siex.StartupInfo.cb = sizeof(STARTUPINFOEXW); + siex.lpAttributeList = pAttributeList; + + // Get the current module path to find the child exe + wchar_t modulePath[MAX_PATH]; + GetModuleFileNameW(nullptr, modulePath, MAX_PATH); + + // Remove the exe name to get just the directory + wchar_t *lastSlash = wcsrchr(modulePath, L'\\'); + if (lastSlash) { + *(lastSlash + 1) = L'\0'; + } + + // Construct full path to child executable + wchar_t cmdLine[MAX_PATH]; + swprintf_s(cmdLine, MAX_PATH, L"\"%sdemo_child.exe\"", modulePath); + + printf("[Parent] Launching child process: %ls\n", cmdLine); + + if (!CreateProcessW(nullptr, cmdLine, nullptr, nullptr, FALSE, + EXTENDED_STARTUPINFO_PRESENT | + CREATE_NEW_CONSOLE, // Use extended startup info + nullptr, nullptr, + &siex.StartupInfo, // Use the extended startup info + &pi)) { + printf("[Parent] Failed to create child process. Error: %lu\n", + GetLastError()); + DeleteProcThreadAttributeList(pAttributeList); + HeapFree(GetProcessHeap(), 0, pAttributeList); + FreeSid(pAppContainerSid); + CloseHandle(hPipe); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + printf("[Parent] Child process created. PID: %lu\n", pi.dwProcessId); + + // Clean up attribute list and AppContainer SID + DeleteProcThreadAttributeList(pAttributeList); + HeapFree(GetProcessHeap(), 0, pAttributeList); + FreeSid(pAppContainerSid); + // Clean up security descriptor - no longer needed + LocalFree(sa.lpSecurityDescriptor); + + // Wait for child to connect to the pipe + printf("[Parent] Waiting for child to connect...\n"); + + if (!ConnectNamedPipe(hPipe, nullptr)) { + DWORD error = GetLastError(); + if (error != ERROR_PIPE_CONNECTED) { + printf("[Parent] Failed to connect to pipe. Error: %lu\n", error); + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + } + + printf("[Parent] Child connected to pipe\n"); + + char buffer[BUFFER_SIZE]; + + // Send first message + if (!SendMessage(hPipe, "Hello from parent!")) { + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + + // Receive response + if (!ReceiveMessage(hPipe, buffer, BUFFER_SIZE)) { + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + + // Send second message + if (!SendMessage(hPipe, "This is message number 2")) { + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + + // Receive response + if (!ReceiveMessage(hPipe, buffer, BUFFER_SIZE)) { + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + + // Send exit message + printf("[Parent] Sending exit command\n"); + SendMessage(hPipe, "EXIT"); + + // Close pipe + printf("[Parent] Closing pipe\n"); + FlushFileBuffers(hPipe); + DisconnectNamedPipe(hPipe); + CloseHandle(hPipe); + + // Wait for child to exit (wait indefinitely for user to close child console) + printf("[Parent] Waiting for child process to exit...\n"); + printf("[Parent] (Child console is waiting for user input - press Enter in " + "child window)\n"); + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exitCode; + GetExitCodeProcess(pi.hProcess, &exitCode); + printf("[Parent] Child process exited with code: %lu\n", exitCode); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + printf("[Parent] Parent process exiting\n"); + + // Wait for user input before closing the parent console + printf("\n[Parent] *** Press Enter to close this window... ***\n"); + getchar(); + + return 0; +} + +bool SendMessage(HANDLE hPipe, const char *message) { + DWORD bytesWritten; + DWORD messageLen = (DWORD)strlen(message) + 1; + + printf("[Host] Sending: %s\n", message); + + if (!WriteFile(hPipe, message, messageLen, &bytesWritten, nullptr)) { + printf("[Host] Failed to write to pipe. Error: %lu\n", GetLastError()); + return false; + } + + return true; +} + +bool ReceiveMessage(HANDLE hPipe, char *buffer, DWORD bufferSize) { + DWORD bytesRead; + + if (!ReadFile(hPipe, buffer, bufferSize, &bytesRead, nullptr)) { + printf("[Parent] Failed to read from pipe. Error: %lu\n", GetLastError()); + return false; + } + + printf("[Parent] Received: %s\n", buffer); + return true; +} diff --git a/Samples/MSIXSamples/SandboxIPC/demo_host.vcxproj b/Samples/MSIXSamples/SandboxIPC/demo_host.vcxproj new file mode 100644 index 000000000..2f792f4cd --- /dev/null +++ b/Samples/MSIXSamples/SandboxIPC/demo_host.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D} + Win32Proj + DemoHost + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + demo_host + + + false + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + demo_host + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + kernel32.lib;user32.lib;advapi32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + kernel32.lib;user32.lib;advapi32.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file diff --git a/Samples/Win7Samples/web/winhttp/WinhttpProxySample/BasicProxyMain.cpp b/Samples/Win7Samples/web/winhttp/WinhttpProxySample/BasicProxyMain.cpp index 97fd24a4c..c1d118aba 100644 --- a/Samples/Win7Samples/web/winhttp/WinhttpProxySample/BasicProxyMain.cpp +++ b/Samples/Win7Samples/web/winhttp/WinhttpProxySample/BasicProxyMain.cpp @@ -272,7 +272,7 @@ Return Value: --*/ -{ +{ DWORD dwError = ERROR_SUCCESS; DWORD cbHost = 0; DWORD cbPath = 0; @@ -502,7 +502,7 @@ wmain( if (pProxyResolver != NULL) { delete pProxyResolver; - pProxyResolver = NULL; + pProxyResolver = NULL; } if (pwszHost != NULL) diff --git a/Samples/Win7Samples/winbase/FSRM/EnumClassificationProperties/cpp/FSRM_Get_Enum_Properties.cpp b/Samples/Win7Samples/winbase/FSRM/EnumClassificationProperties/cpp/FSRM_Get_Enum_Properties.cpp index da04f2474..2367f6298 100644 --- a/Samples/Win7Samples/winbase/FSRM/EnumClassificationProperties/cpp/FSRM_Get_Enum_Properties.cpp +++ b/Samples/Win7Samples/winbase/FSRM/EnumClassificationProperties/cpp/FSRM_Get_Enum_Properties.cpp @@ -222,7 +222,7 @@ HRESULT DisplayPropertyDefinition( return hr; } - + /*++ Routine EnumerateProperties