@holoscript/holosystem 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -3
- package/bin/holosystem.mjs +21 -0
- package/docs/vm-launch-threat-model.md +91 -23
- package/native/windows-sandbox/Canary.c +107 -0
- package/native/windows-sandbox/Program.cs +1117 -0
- package/native/windows-sandbox/README.md +30 -0
- package/native/windows-sandbox/build.ps1 +48 -0
- package/native/windows-x64/holosystem-appcontainer-canary.exe +0 -0
- package/native/windows-x64/holosystem-sandbox-launcher.exe +0 -0
- package/package.json +2 -1
- package/src/index.mjs +20 -0
- package/src/vm-launch.mjs +684 -28
package/README.md
CHANGED
|
@@ -511,6 +511,30 @@ AMD64 host, `qemu-system-x86_64.exe`, q35, 128 MiB, one CPU, and exactly two
|
|
|
511
511
|
launches. Plans cannot provide a command, argument, environment variable,
|
|
512
512
|
device, network, firmware, monitor, display, or fallback setting.
|
|
513
513
|
|
|
514
|
+
`vm-launch-whpx-sandboxed` is a third, separately named vocabulary. It retains
|
|
515
|
+
the explicit WHPX contract and additionally launches QEMU through the measured
|
|
516
|
+
Windows helper shipped in this package. The helper creates a
|
|
517
|
+
`CreateRestrictedToken(DISABLE_MAX_PRIVILEGE)` filtered token, lowers it to low
|
|
518
|
+
integrity, admits at most the single Windows pass-through privilege, creates
|
|
519
|
+
the process suspended with an explicit three-handle allowlist, assigns it to a
|
|
520
|
+
kill-on-close Job Object with one-process, memory, and UI restrictions, and
|
|
521
|
+
only then resumes it. A private low-integrity temporary directory is the only
|
|
522
|
+
intended writable launch location.
|
|
523
|
+
|
|
524
|
+
`vm-launch-whpx-appcontainer` is a fourth, separately named vocabulary. It
|
|
525
|
+
retains those restricted-token and Job controls, gives an ephemeral Windows
|
|
526
|
+
AppContainer identity zero capabilities, grants that identity only read/execute
|
|
527
|
+
access to the measured launch snapshot and modify access to its private temp
|
|
528
|
+
directory, and requires file-read plus loopback-connect attack canaries to fail
|
|
529
|
+
before either WHPX launch begins. The plan cannot name a capability, path,
|
|
530
|
+
command, network mode, or fallback.
|
|
531
|
+
|
|
532
|
+
`vm-launch-appcontainer` uses the same zero-capability boundary with QEMU TCG.
|
|
533
|
+
It fixes the translation buffer at 64 MiB so the one-process 512 MiB Job limit
|
|
534
|
+
remains effective. This is the fully confined adapter proven on the validated
|
|
535
|
+
Windows host: it proves the machine VM and host file/network boundary, but sets
|
|
536
|
+
`hardwareBacked: false` and keeps hardware acceleration missing.
|
|
537
|
+
|
|
514
538
|
First measure the complete caller-owned QEMU runtime closure and both guest
|
|
515
539
|
artifacts:
|
|
516
540
|
|
|
@@ -567,6 +591,44 @@ arguments for the WHPX schema. WHPX requires the Windows Hypervisor Platform
|
|
|
567
591
|
API exposed to QEMU; see the [QEMU WHPX documentation](https://www.qemu.org/docs/master/system/whpx.html)
|
|
568
592
|
and [Microsoft's Windows Hypervisor Platform API overview](https://learn.microsoft.com/en-us/virtualization/api/).
|
|
569
593
|
|
|
594
|
+
For the sandboxed adapter, use schema
|
|
595
|
+
`holoscript.holosystem.whpx-sandboxed-vm-launch-plan.v1`, add the following
|
|
596
|
+
closed field, and call `vm-launch-whpx-sandboxed`:
|
|
597
|
+
|
|
598
|
+
```json
|
|
599
|
+
{
|
|
600
|
+
"sandbox": {
|
|
601
|
+
"kind": "windows-low-integrity-job-v1",
|
|
602
|
+
"launcherDigest": "sha256:<HOLOSYSTEM_WINDOWS_SANDBOX_LAUNCHER_DIGEST>"
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
The exported `HOLOSYSTEM_WINDOWS_SANDBOX_LAUNCHER_DIGEST` is the digest of the
|
|
608
|
+
package's audited launcher binary. A plan with another digest is blocked before
|
|
609
|
+
process creation. The launcher source and local rebuild recipe are shipped in
|
|
610
|
+
`native/windows-sandbox`; the measured binary is shipped in
|
|
611
|
+
`native/windows-x64`.
|
|
612
|
+
|
|
613
|
+
For the AppContainer adapter, use schema
|
|
614
|
+
`holoscript.holosystem.whpx-appcontainer-vm-launch-plan.v1`, use the same
|
|
615
|
+
launcher digest with sandbox kind `windows-appcontainer-deny-v1`, and call
|
|
616
|
+
`vm-launch-whpx-appcontainer`. The launcher creates and deletes a fresh profile
|
|
617
|
+
for each launch. Its closed receipt protocol must prove the child AppContainer
|
|
618
|
+
SID, zero capabilities, low-integrity filtered token, snapshot/temp grants,
|
|
619
|
+
access-denied (`5`) for a protected caller-readable file, plus either explicit
|
|
620
|
+
WSA access-denied (`10013`) or a bounded WFP-style packet-drop timeout (`10060`)
|
|
621
|
+
with no accepted live loopback connection. Any missing or changed observation
|
|
622
|
+
blocks the receipt.
|
|
623
|
+
|
|
624
|
+
Use schema `holoscript.holosystem.appcontainer-vm-launch-plan.v1`, target
|
|
625
|
+
accelerator `tcg`, the same two sandbox digests, and command
|
|
626
|
+
`vm-launch-appcontainer` for the software-emulated confined lane. On the
|
|
627
|
+
validated Windows 11 host, the WHPX AppContainer form fails closed because
|
|
628
|
+
Windows returns `0x80070005` when zero-capability QEMU initializes WHPX. The
|
|
629
|
+
package does not merge that result with the separately proven hardware-backed
|
|
630
|
+
low-integrity WHPX receipt.
|
|
631
|
+
|
|
570
632
|
The runner creates a private snapshot and remeasures the complete QEMU closure,
|
|
571
633
|
kernel, and initramfs before and after each launch. It then generates QEMU
|
|
572
634
|
arguments that disable user configuration, default devices, networking, USB,
|
|
@@ -583,21 +645,49 @@ A verified TCG receipt includes `machine-vm-launch`,
|
|
|
583
645
|
`hardwareBacked: false`, and leaves hardware acceleration missing. A verified
|
|
584
646
|
WHPX receipt additionally includes `hardware-hypervisor-acceleration` and sets
|
|
585
647
|
`hardwareBacked: true` only after two explicit WHPX launches succeed. Both
|
|
586
|
-
adapters leave `host-process-isolation` missing: QEMU still has the ambient
|
|
648
|
+
ambient adapters leave `host-process-isolation` missing: QEMU still has the ambient
|
|
587
649
|
rights of the Windows process. Receipts therefore expose
|
|
588
650
|
`isolation.hostProcess: "ambient-windows-process"` and
|
|
589
651
|
`isolation.verified: false` even when WHPX acceleration is verified. Neither
|
|
590
|
-
receipt proves an IOMMU, measured boot, firmware authenticity,
|
|
591
|
-
memory, or side-channel resistance.
|
|
652
|
+
ambient adapter receipt proves an IOMMU, measured boot, firmware authenticity,
|
|
653
|
+
confidential memory, or side-channel resistance.
|
|
654
|
+
|
|
655
|
+
A verified `vm-launch-whpx-sandboxed` receipt includes
|
|
656
|
+
`host-process-isolation` only for the declared integrity, privilege,
|
|
657
|
+
inherited-handle, lifetime, process-count, memory, and UI scope. It deliberately
|
|
658
|
+
leaves `host-filesystem-confidentiality` and `host-network-isolation` missing:
|
|
659
|
+
low integrity prevents writes up to medium-integrity objects but does not
|
|
660
|
+
prevent reads, and the filtered token is not an AppContainer network capability
|
|
661
|
+
boundary. The sandboxed receipt also does not prove the broader hardware and
|
|
662
|
+
firmware properties named above. The
|
|
592
663
|
exact claims are in the [VM launch threat model](./docs/vm-launch-threat-model.md).
|
|
593
664
|
|
|
665
|
+
A verified `vm-launch-whpx-appcontainer` receipt additionally includes
|
|
666
|
+
`host-filesystem-confidentiality` and `host-network-isolation` for the tested
|
|
667
|
+
zero-capability AppContainer boundary. Those are scoped claims: they do not
|
|
668
|
+
establish QEMU provenance, host-kernel correctness, IOMMU separation, measured
|
|
669
|
+
boot, firmware authenticity, crash-dump custody, confidential memory, or
|
|
670
|
+
side-channel resistance.
|
|
671
|
+
|
|
672
|
+
A verified `vm-launch-appcontainer` receipt includes the same process,
|
|
673
|
+
filesystem, and network layers while leaving
|
|
674
|
+
`hardware-hypervisor-acceleration` missing. Receipts describe one executed
|
|
675
|
+
boundary, not the union of properties observed in different launches.
|
|
676
|
+
|
|
594
677
|
```js
|
|
595
678
|
import {
|
|
596
679
|
inspectVmExecutor,
|
|
680
|
+
inspectAppContainerVmLaunchPlan,
|
|
597
681
|
inspectVmLaunchAsset,
|
|
598
682
|
inspectVmLaunchPlan,
|
|
683
|
+
inspectWindowsVmSandboxLauncher,
|
|
684
|
+
inspectWhpxAppContainerVmLaunchPlan,
|
|
685
|
+
inspectWhpxSandboxedVmLaunchPlan,
|
|
599
686
|
inspectWhpxVmLaunchPlan,
|
|
600
687
|
runVmLaunch,
|
|
688
|
+
runAppContainerVmLaunch,
|
|
689
|
+
runWhpxAppContainerVmLaunch,
|
|
690
|
+
runWhpxSandboxedVmLaunch,
|
|
601
691
|
runWhpxVmLaunch,
|
|
602
692
|
} from '@holoscript/holosystem';
|
|
603
693
|
```
|
package/bin/holosystem.mjs
CHANGED
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
inspectVmExecutor,
|
|
15
15
|
inspectVmLaunchAsset,
|
|
16
16
|
runNativeBuild,
|
|
17
|
+
runAppContainerVmLaunch,
|
|
17
18
|
runVmLaunch,
|
|
19
|
+
runWhpxAppContainerVmLaunch,
|
|
20
|
+
runWhpxSandboxedVmLaunch,
|
|
18
21
|
runWhpxVmLaunch,
|
|
19
22
|
} from '../src/index.mjs';
|
|
20
23
|
|
|
@@ -34,7 +37,10 @@ Usage:
|
|
|
34
37
|
holosystem vm-executor --runtime <directory> [--json]
|
|
35
38
|
holosystem vm-asset --kind <kernel|initrd> --file <file> [--json]
|
|
36
39
|
holosystem vm-launch --plan <file> --runtime <directory> --kernel <file> --initrd <file> [--output <receipt>] [--force] [--json]
|
|
40
|
+
holosystem vm-launch-appcontainer --plan <file> --runtime <directory> --kernel <file> --initrd <file> [--output <receipt>] [--force] [--json]
|
|
37
41
|
holosystem vm-launch-whpx --plan <file> --runtime <directory> --kernel <file> --initrd <file> [--output <receipt>] [--force] [--json]
|
|
42
|
+
holosystem vm-launch-whpx-sandboxed --plan <file> --runtime <directory> --kernel <file> --initrd <file> [--output <receipt>] [--force] [--json]
|
|
43
|
+
holosystem vm-launch-whpx-appcontainer --plan <file> --runtime <directory> --kernel <file> --initrd <file> [--output <receipt>] [--force] [--json]
|
|
38
44
|
holosystem substrate --input <file> [--output <file>] [--force] [--json]
|
|
39
45
|
holosystem --help
|
|
40
46
|
holosystem --version
|
|
@@ -701,11 +707,26 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
701
707
|
runVmAsset(argv.slice(1));
|
|
702
708
|
} else if (command === 'vm-launch') {
|
|
703
709
|
runVmLaunchCommand(argv.slice(1));
|
|
710
|
+
} else if (command === 'vm-launch-appcontainer') {
|
|
711
|
+
runVmLaunchCommand(argv.slice(1), {
|
|
712
|
+
launcher: runAppContainerVmLaunch,
|
|
713
|
+
commandName: 'vm-launch-appcontainer',
|
|
714
|
+
});
|
|
704
715
|
} else if (command === 'vm-launch-whpx') {
|
|
705
716
|
runVmLaunchCommand(argv.slice(1), {
|
|
706
717
|
launcher: runWhpxVmLaunch,
|
|
707
718
|
commandName: 'vm-launch-whpx',
|
|
708
719
|
});
|
|
720
|
+
} else if (command === 'vm-launch-whpx-sandboxed') {
|
|
721
|
+
runVmLaunchCommand(argv.slice(1), {
|
|
722
|
+
launcher: runWhpxSandboxedVmLaunch,
|
|
723
|
+
commandName: 'vm-launch-whpx-sandboxed',
|
|
724
|
+
});
|
|
725
|
+
} else if (command === 'vm-launch-whpx-appcontainer') {
|
|
726
|
+
runVmLaunchCommand(argv.slice(1), {
|
|
727
|
+
launcher: runWhpxAppContainerVmLaunch,
|
|
728
|
+
commandName: 'vm-launch-whpx-appcontainer',
|
|
729
|
+
});
|
|
709
730
|
} else if (command === 'substrate') {
|
|
710
731
|
runSubstrate(argv.slice(1));
|
|
711
732
|
} else {
|
|
@@ -17,6 +17,35 @@ Windows Hypervisor Platform adapter for those launches; it does not prove host
|
|
|
17
17
|
process isolation. QEMU retains the ambient rights of its Windows process in
|
|
18
18
|
both adapters.
|
|
19
19
|
|
|
20
|
+
`vm-launch-whpx-sandboxed` is a separate schema and receipt. It runs the same
|
|
21
|
+
measured WHPX launch behind a measured native Windows launcher that creates a
|
|
22
|
+
filtered token with `DISABLE_MAX_PRIVILEGE`, sets low mandatory integrity,
|
|
23
|
+
verifies at most the single pass-through privilege remains enabled, creates
|
|
24
|
+
QEMU suspended with only NUL/stdout/stderr handles inherited, assigns QEMU to a
|
|
25
|
+
Job Object before resume, and applies kill-on-close, one-process, 512 MiB, and
|
|
26
|
+
full Job UI restrictions. Its `host-process-isolation` claim is limited to
|
|
27
|
+
those controls. It is not an AppContainer and does not claim host filesystem
|
|
28
|
+
confidentiality or host network isolation.
|
|
29
|
+
|
|
30
|
+
`vm-launch-whpx-appcontainer` is another separate schema and receipt. It adds
|
|
31
|
+
an ephemeral zero-capability AppContainer identity to the same filtered,
|
|
32
|
+
low-integrity token and pre-resume Job boundary. The identity receives inherited
|
|
33
|
+
read/execute access to the private measured snapshot and modify access only to
|
|
34
|
+
the private temp directory. Before QEMU starts, the launcher runs an
|
|
35
|
+
AppContainer child under the same token and verifies that it cannot read a
|
|
36
|
+
caller-readable protected sentinel or connect to a live loopback listener. The
|
|
37
|
+
receipt claims filesystem confidentiality and network isolation only when the
|
|
38
|
+
token identity, zero capability count, exact denial errors, and both WHPX boots
|
|
39
|
+
all verify.
|
|
40
|
+
|
|
41
|
+
`vm-launch-appcontainer` applies the same boundary to TCG with a fixed 64 MiB
|
|
42
|
+
translation buffer. It can claim process, filesystem, and network isolation
|
|
43
|
+
after two deterministic boots, but it cannot claim hardware acceleration. On
|
|
44
|
+
the validated Windows 11 host, zero-capability QEMU receives access denied
|
|
45
|
+
(`0x80070005`) while initializing WHPX. That negative result is preserved as a
|
|
46
|
+
blocked WHPX AppContainer receipt; it is never combined with the verified TCG
|
|
47
|
+
AppContainer receipt or the separately verified low-integrity WHPX receipt.
|
|
48
|
+
|
|
20
49
|
## Protected assets and trust boundaries
|
|
21
50
|
|
|
22
51
|
The protected inputs are the declarative plan, the complete caller-owned QEMU
|
|
@@ -39,20 +68,27 @@ than implied away by a successful boot.
|
|
|
39
68
|
|
|
40
69
|
## Adversary specifications
|
|
41
70
|
|
|
42
|
-
| Adversary action | Required result
|
|
43
|
-
| ---------------------------------------------------------------------------------------------------- |
|
|
44
|
-
| Add a shell command, argument vector, environment field, device, network mode, or unknown plan field | Reject before process launch
|
|
45
|
-
| Put WHPX in a TCG plan, TCG in a WHPX plan, or request a fallback | Reject rather than downgrade, cross-route, or overclaim
|
|
46
|
-
| Replace any QEMU executable, DLL, firmware file, kernel, or initramfs before measurement | Reject before process launch
|
|
47
|
-
| Replace measured files while the launch snapshot is being created | Reject the snapshot
|
|
48
|
-
| Replace caller-owned files after the first launch | Second launch still uses the same private snapshot
|
|
49
|
-
| Persistently self-modify the private runtime or guest artifacts during a launch | Reject on post-launch remeasurement
|
|
50
|
-
| Inject QEMU behavior through inherited environment variables or user configuration | Launch with a minimal environment and `-no-user-config`
|
|
51
|
-
| Obtain implicit host devices or a communication channel | Start with `-nodefaults`, `-nic none`, USB off, no display, and no monitor
|
|
52
|
-
| Forge the guest signal, use the wrong exit path, or change adapter diagnostics | Reject the receipt and retain no raw output
|
|
53
|
-
| Make clean launches observably disagree | Reject deterministic coverage
|
|
54
|
-
| Inject a fake process runner through the public API | Ignore it and execute only the bound process implementation
|
|
55
|
-
|
|
|
71
|
+
| Adversary action | Required result | Receipt or test evidence |
|
|
72
|
+
| ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
73
|
+
| Add a shell command, argument vector, environment field, device, network mode, or unknown plan field | Reject before process launch | `vm-launch-field-unknown`; closed-vocabulary test |
|
|
74
|
+
| Put WHPX in a TCG plan, TCG in a WHPX plan, or request a fallback | Reject rather than downgrade, cross-route, or overclaim | Separate schemas; accelerator/unknown-field tests |
|
|
75
|
+
| Replace any QEMU executable, DLL, firmware file, kernel, or initramfs before measurement | Reject before process launch | Runtime, binary, kernel, and initrd digest mismatch issues |
|
|
76
|
+
| Replace measured files while the launch snapshot is being created | Reject the snapshot | Runtime, kernel, and initrd snapshot mismatch issues |
|
|
77
|
+
| Replace caller-owned files after the first launch | Second launch still uses the same private snapshot | Private-snapshot substitution test |
|
|
78
|
+
| Persistently self-modify the private runtime or guest artifacts during a launch | Reject on post-launch remeasurement | Private-snapshot drift test |
|
|
79
|
+
| Inject QEMU behavior through inherited environment variables or user configuration | Launch with a minimal environment and `-no-user-config` | Generated-argv test |
|
|
80
|
+
| Obtain implicit host devices or a communication channel | Start with `-nodefaults`, `-nic none`, USB off, no display, and no monitor | Generated-argv test |
|
|
81
|
+
| Forge the guest signal, use the wrong exit path, or change adapter diagnostics | Reject the receipt and retain no raw output | Console, exit-code, and diagnostic-digest tests |
|
|
82
|
+
| Make clean launches observably disagree | Reject deterministic coverage | `vm-launch-nondeterministic` |
|
|
83
|
+
| Inject a fake process runner through the public API | Ignore it and execute only the bound process implementation | Public process-runner injection test |
|
|
84
|
+
| Select ambient execution, weaken a sandbox control, change the launcher, or forge its protocol | Reject the distinct sandbox schema, launcher digest, or incomplete evidence | Closed sandbox vocabulary and protocol-forgery tests |
|
|
85
|
+
| Pass inherited parent handles into sandboxed QEMU | Admit only NUL input and the two bounded output pipes | Native handle-list evidence and receipt assertion |
|
|
86
|
+
| Spawn another process or escape launcher lifetime/resource bounds | Assign suspended QEMU before resume to a one-process, kill-on-close Job | Native Job evidence and adversarial receipt tests |
|
|
87
|
+
| Add an AppContainer capability, path grant, network mode, command, or fallback | Reject the distinct closed plan vocabulary before launch | AppContainer closed-vocabulary test |
|
|
88
|
+
| Forge the AppContainer SID, capability count, profile cleanup, or grant evidence | Reject the closed native protocol | AppContainer protocol-forgery test |
|
|
89
|
+
| Read a caller-readable file outside the measured snapshot | Require exact access-denied evidence from a zero-capability AppContainer | Protected-sentinel canary and receipt assertion |
|
|
90
|
+
| Connect to the host loopback listener from compromised QEMU | Require bounded WSA deny/drop evidence and no accepted connection | Loopback canary and receipt assertion |
|
|
91
|
+
| Put an operational path or guest output in the receipt | Withhold it; report only digest and byte count | Receipt disclosure assertions |
|
|
56
92
|
|
|
57
93
|
## Fixed launch policy
|
|
58
94
|
|
|
@@ -73,6 +109,27 @@ produce the plan-pinned diagnostic digest after removal of only the exact,
|
|
|
73
109
|
randomized private-executor prefix that QEMU repeats at diagnostic line starts. Raw
|
|
74
110
|
stdout and stderr are never included in the receipt.
|
|
75
111
|
|
|
112
|
+
The sandboxed adapter additionally copies the package-pinned launcher into the
|
|
113
|
+
private measured snapshot. The launcher protocol is closed and binary-safe: it
|
|
114
|
+
returns base64 child streams plus derived control evidence, and HoloSystem
|
|
115
|
+
rejects unknown fields, non-canonical base64, an unbounded enabled-privilege
|
|
116
|
+
count, a missing control, or launcher stderr. Launcher and QEMU snapshots are
|
|
117
|
+
remeasured before and after both launches.
|
|
118
|
+
|
|
119
|
+
The AppContainer adapter uses the same measured launcher but a distinct plan,
|
|
120
|
+
protocol, and control vocabulary. It never accepts caller-specified capabilities
|
|
121
|
+
or ACL paths. Each native invocation creates a fresh AppContainer profile,
|
|
122
|
+
applies the fixed grants, runs both canaries, creates QEMU with the same zero
|
|
123
|
+
capability `SECURITY_CAPABILITIES`, checks the suspended child token, and deletes
|
|
124
|
+
the profile during cleanup. HoloSystem rejects a nonzero capability count,
|
|
125
|
+
unexpected bounded denial code, accepted loopback connection, incomplete cleanup, or
|
|
126
|
+
any unknown protocol field.
|
|
127
|
+
|
|
128
|
+
The TCG AppContainer plan also fixes `tcg,tb-size=64`; a caller cannot enlarge
|
|
129
|
+
the JIT translation buffer or relax the 512 MiB process limit. Its receipt keeps
|
|
130
|
+
`hardware-hypervisor-acceleration` missing even when every confinement canary
|
|
131
|
+
passes.
|
|
132
|
+
|
|
76
133
|
## Residual risk and next layer
|
|
77
134
|
|
|
78
135
|
The QEMU runtime closure includes firmware data files and DLLs, so substitution
|
|
@@ -80,12 +137,23 @@ changes its digest. Hashing does not establish provenance or measured boot.
|
|
|
80
137
|
Windows QEMU also cannot disable host crash-dump capture through this contract;
|
|
81
138
|
`host-crash-dump-custody` remains an explicit boundary.
|
|
82
139
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
140
|
+
The ambient adapters do not place QEMU behind an OS process sandbox.
|
|
141
|
+
`virtual-device-minimization` therefore describes only their generated
|
|
142
|
+
guest-facing virtual device surface.
|
|
143
|
+
|
|
144
|
+
The sandboxed adapter is narrower than AppContainer or a separate host VM. Its
|
|
145
|
+
filtered token retains normal read access allowed to the caller and does not
|
|
146
|
+
create an OS network capability boundary. The QEMU command still contains no
|
|
147
|
+
guest network device, but a compromised host QEMU process could use host
|
|
148
|
+
network APIs. `host-filesystem-confidentiality` and `host-network-isolation`
|
|
149
|
+
therefore remain explicit missing layers even when the declared process
|
|
150
|
+
isolation controls verify. The launcher binary is measured, but source-to-binary
|
|
151
|
+
reproducibility and code signing remain part of `qemu-runtime-supply-chain` and
|
|
152
|
+
host correctness trust.
|
|
153
|
+
|
|
154
|
+
The TCG AppContainer tracer implements capability-scoped filesystem and network
|
|
155
|
+
denial without hardware acceleration; the WHPX form remains blocked on the
|
|
156
|
+
validated host. It does not turn the Windows kernel into a separate trust
|
|
157
|
+
domain. The next layers are a WHPX broker or independent host boundary and
|
|
158
|
+
firmware/measured-boot evidence. IOMMU, device-assignment, crash-dump custody,
|
|
159
|
+
host correctness, and side-channel boundaries remain separate.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#define WIN32_LEAN_AND_MEAN
|
|
2
|
+
#include <winsock2.h>
|
|
3
|
+
#include <windows.h>
|
|
4
|
+
#include <stdio.h>
|
|
5
|
+
#include <wchar.h>
|
|
6
|
+
|
|
7
|
+
static int read_token_integer(HANDLE token, TOKEN_INFORMATION_CLASS information_class, int fallback)
|
|
8
|
+
{
|
|
9
|
+
DWORD required = 0;
|
|
10
|
+
BYTE *buffer;
|
|
11
|
+
int value;
|
|
12
|
+
GetTokenInformation(token, information_class, NULL, 0, &required);
|
|
13
|
+
if (required == 0) return fallback;
|
|
14
|
+
buffer = (BYTE *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, required);
|
|
15
|
+
if (buffer == NULL) return fallback;
|
|
16
|
+
if (!GetTokenInformation(token, information_class, buffer, required, &required)) {
|
|
17
|
+
HeapFree(GetProcessHeap(), 0, buffer);
|
|
18
|
+
return fallback;
|
|
19
|
+
}
|
|
20
|
+
value = *(int *)buffer;
|
|
21
|
+
HeapFree(GetProcessHeap(), 0, buffer);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
int wmain(int argc, wchar_t **argv)
|
|
26
|
+
{
|
|
27
|
+
HANDLE file = INVALID_HANDLE_VALUE;
|
|
28
|
+
HANDLE token = NULL;
|
|
29
|
+
WSADATA socket_data;
|
|
30
|
+
SOCKET socket_handle = INVALID_SOCKET;
|
|
31
|
+
struct sockaddr_in address;
|
|
32
|
+
fd_set writable;
|
|
33
|
+
fd_set exceptional;
|
|
34
|
+
struct timeval wait_time;
|
|
35
|
+
u_long nonblocking = 1;
|
|
36
|
+
int port;
|
|
37
|
+
int filesystem_error = 0;
|
|
38
|
+
int network_error = 0;
|
|
39
|
+
int app_container = 0;
|
|
40
|
+
int capability_count = -1;
|
|
41
|
+
|
|
42
|
+
if (argc != 5 || wcscmp(argv[1], L"--protected-sentinel") != 0 ||
|
|
43
|
+
wcscmp(argv[3], L"--loopback-port") != 0) return 64;
|
|
44
|
+
port = _wtoi(argv[4]);
|
|
45
|
+
if (port < 1 || port > 65535) return 64;
|
|
46
|
+
|
|
47
|
+
file = CreateFileW(
|
|
48
|
+
argv[2], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
|
49
|
+
FILE_ATTRIBUTE_NORMAL, NULL
|
|
50
|
+
);
|
|
51
|
+
if (file == INVALID_HANDLE_VALUE) filesystem_error = (int)GetLastError();
|
|
52
|
+
else CloseHandle(file);
|
|
53
|
+
|
|
54
|
+
if (WSAStartup(MAKEWORD(2, 2), &socket_data) != 0) {
|
|
55
|
+
network_error = WSAGetLastError();
|
|
56
|
+
} else {
|
|
57
|
+
socket_handle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
58
|
+
if (socket_handle == INVALID_SOCKET) {
|
|
59
|
+
network_error = WSAGetLastError();
|
|
60
|
+
} else {
|
|
61
|
+
ioctlsocket(socket_handle, FIONBIO, &nonblocking);
|
|
62
|
+
ZeroMemory(&address, sizeof(address));
|
|
63
|
+
address.sin_family = AF_INET;
|
|
64
|
+
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
65
|
+
address.sin_port = htons((u_short)port);
|
|
66
|
+
if (connect(socket_handle, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR) {
|
|
67
|
+
network_error = WSAGetLastError();
|
|
68
|
+
if (network_error == WSAEWOULDBLOCK) {
|
|
69
|
+
FD_ZERO(&writable);
|
|
70
|
+
FD_ZERO(&exceptional);
|
|
71
|
+
FD_SET(socket_handle, &writable);
|
|
72
|
+
FD_SET(socket_handle, &exceptional);
|
|
73
|
+
wait_time.tv_sec = 2;
|
|
74
|
+
wait_time.tv_usec = 0;
|
|
75
|
+
if (select(0, NULL, &writable, &exceptional, &wait_time) > 0) {
|
|
76
|
+
int socket_error = 0;
|
|
77
|
+
int error_length = sizeof(socket_error);
|
|
78
|
+
if (getsockopt(
|
|
79
|
+
socket_handle, SOL_SOCKET, SO_ERROR,
|
|
80
|
+
(char *)&socket_error, &error_length
|
|
81
|
+
) == 0) network_error = socket_error;
|
|
82
|
+
else network_error = WSAGetLastError();
|
|
83
|
+
} else {
|
|
84
|
+
network_error = WSAETIMEDOUT;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
closesocket(socket_handle);
|
|
89
|
+
}
|
|
90
|
+
WSACleanup();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
|
|
94
|
+
app_container = read_token_integer(token, TokenIsAppContainer, 0);
|
|
95
|
+
capability_count = read_token_integer(token, TokenCapabilities, -1);
|
|
96
|
+
CloseHandle(token);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
printf(
|
|
100
|
+
"appContainer=%d;capabilityCount=%d;filesystemError=%d;networkError=%d\n",
|
|
101
|
+
app_container != 0 ? 1 : 0,
|
|
102
|
+
capability_count,
|
|
103
|
+
filesystem_error,
|
|
104
|
+
network_error
|
|
105
|
+
);
|
|
106
|
+
return 0;
|
|
107
|
+
}
|