Skip to content

Commit 766a0e2

Browse files
fix: harden Windows broker security and validation (#66)
1 parent 8e076f3 commit 766a0e2

17 files changed

Lines changed: 1304 additions & 158 deletions

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ endif()
157157
# Package config
158158
#
159159
if(LIBVIRTUALHID_INSTALL)
160+
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE"
161+
DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/libvirtualhid"
162+
COMPONENT library)
163+
160164
configure_package_config_file(
161165
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/libvirtualhid-config.cmake.in"
162166
"${CMAKE_CURRENT_BINARY_DIR}/libvirtualhid-config.cmake"

LICENSES/README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ Windows driver package.
77
- Cross-platform library source, public headers, non-driver backends, examples,
88
tests, build scripts, and documentation unless listed below:
99
[MIT](https://github.com/LizardByte/libvirtualhid/blob/master/LICENSE).
10-
- Windows UMDF driver source under `src/platform/windows/driver/`:
10+
- Windows UMDF driver source under `src/platform/windows/driver/`, the broker
11+
service under `src/platform/windows/broker/`, and the broker entitlement and
12+
evaluation sources
13+
`src/platform/windows/shared/lvh_windows_broker_config.hpp` and
14+
`src/platform/windows/shared/lvh_windows_github_actions_evaluation.hpp`:
1115
[LizardByte Source-Available License 1.0](https://github.com/LizardByte/libvirtualhid/blob/master/LICENSES/LicenseRef-LizardByte-SAL-1.0.md).
1216
- Generated Windows driver package artifacts, including the driver MSI:
1317
[LizardByte Source-Available License 1.0](https://github.com/LizardByte/libvirtualhid/blob/master/LICENSES/LicenseRef-LizardByte-SAL-1.0.md).
1418

15-
The Windows driver MSI may include MIT-licensed helper components from this
16-
repository; packaged installs include both license texts for that reason.
19+
Every installed library distribution includes the MIT notice under
20+
`share/licenses/libvirtualhid`. The Windows driver MSI may include MIT-licensed
21+
helper components from this repository; packaged installs include both license
22+
texts for that reason.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ test app path and does not currently advertise an ARM64 build.
173173
## 📄 License
174174
175175
The cross-platform `libvirtualhid` library is licensed under the MIT License.
176-
The Windows UMDF driver source and generated Windows
177-
driver package artifacts, including the driver MSI, are licensed under the
178-
LizardByte Source-Available License 1.0 (LB-SAL 1.0). See the
176+
The Windows UMDF driver, broker, proprietary entitlement/evaluation sources,
177+
and generated Windows driver package artifacts, including the driver MSI, are
178+
licensed under the LizardByte Source-Available License 1.0 (LB-SAL 1.0). See the
179179
[license map](LICENSES/README.md) for the full repository split.

docs/platform-support.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ effects back into the public callback. Each requested playback repetition
107107
restarts the effect's ramp and envelope timing. A zero-length effect remains
108108
active until its explicit stop event, matching the infinite-effect contract used
109109
by SDL and Steam. The Linux backend lets a new uinput device settle before
110-
reading those effects so an early poll error cannot disable feedback for the
110+
reading those effects, so an early poll error cannot disable feedback for the
111111
device lifetime. Generated UHID nodes are correlated by stable physical and
112112
unique identifiers when available, with device-name matching used only as a
113113
fallback. PlayStation rumble is read from native UHID interrupt-channel output
@@ -120,7 +120,7 @@ which avoids changing the raw button capability surface. It uses a compact
120120
Generic button layout rather than the sparse Xbox button slots.
121121

122122
Xbox 360 retains its `0x045E:0x028E` identity, while its Linux uinput device uses
123-
the Bluetooth bus so consumers select the sparse button mapping.
123+
the Bluetooth bus, so consumers select the sparse button mapping.
124124
Xbox One and Xbox Series retain their public USB identities, but their Linux
125125
uinput devices use the corresponding Bluetooth product identities (`0x0B20`
126126
and `0x0B13`, respectively), whose standard consumer mappings match the events
@@ -230,7 +230,7 @@ Current macOS capabilities:
230230
codes.
231231
- Mouse relative movement, absolute movement on the main display, left/middle/
232232
right button transitions, and pixel-based vertical/horizontal scroll.
233-
- Shared keyboard modifier state on mouse events so combinations such as
233+
- Shared keyboard modifier state on mouse events, so combinations such as
234234
shift-click continue to work.
235235

236236
Unsupported macOS capabilities currently return `unsupported_profile`:

docs/store-review-validation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ C:\Program Files\libvirtualhid\services\windows\libvirtualhid_broker.exe
2929
3030
Required validation:
3131
$installRoot = Join-Path $env:ProgramFiles "libvirtualhid"
32-
& "$installRoot\tools\windows\virtualhid_control.exe"
32+
Start-Process "$installRoot\tools\windows\virtualhid_control.exe"
3333
3434
In the libvirtualhid control window, paste the supplied review key into the License key field and click Activate license. Confirm the status changes to Licensed. Then leave the default Xbox Series profile selected and click Create. Use the button and axis controls in the UI to submit input to the virtual controller.
3535

docs/usage.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -104,23 +104,21 @@ buttons, sticks, triggers, and battery state, show backend and profile
104104
capabilities, list device nodes reported for UI-created devices, and display
105105
normalized gamepad output such as rumble, RGB LED, adaptive trigger, trigger
106106
rumble, and raw report events delivered through the normal callback path. Button
107-
controls are momentary by default so they behave like physical gamepad buttons;
107+
controls are momentary by default, so they behave like physical gamepad buttons;
108108
on Windows, the UI also displays broker license status and can activate,
109-
refresh, or deactivate a machine license. Outside the explicitly marked GitHub
110-
Actions test environment, every Windows UMDF gamepad creation requires a
111-
current successful license validation response and there is no offline grace
112-
period. The CI-only exception is a single five-minute window that begins with
113-
the first gamepad creation attempt. Purchase and account-management buttons use
109+
refresh, or deactivate a machine license without elevation. Every Windows UMDF
110+
gamepad creation requires a current successful license validation response and
111+
there is no offline grace period. Purchase and account-management buttons use
114112
the compiled URLs in
115113
`src/platform/windows/shared/lvh_windows_broker_config.hpp`.
116-
enable `Lock buttons` to keep the old click-to-toggle behavior for held inputs.
114+
Enable `Lock buttons` to click-to-toggle behavior for held inputs.
117115
The resizable window supports a compact width. Its device and control panels
118-
stack, and the button grid reflows, to keep controls usable when it is narrowed.
119-
The UI intentionally does not use gamepad navigation so virtual devices created
116+
stack, and the button grid reflows to keep controls usable when it is narrowed.
117+
The UI intentionally does not use gamepad navigation, so virtual devices created
120118
by the tool cannot drive the tool's own controls.
121119

122120
External devices created by another process, such as Sunshine, are not
123-
enumerated yet. That requires backend protocol support so the Windows driver or
121+
enumerated yet. That requires backend protocol support, so the Windows driver or
124122
Linux backend can expose cross-process device snapshots without letting two
125123
processes race to control the same virtual device.
126124

@@ -133,7 +131,9 @@ The API centers on portable device concepts:
133131
- `get_license_status`, `activate_license`, `validate_license`, and
134132
`deactivate_license`: provider-neutral machine license operations for host
135133
applications. On Windows these call the installed local broker; license keys
136-
are not retained by the client library or returned to the application.
134+
are not retained by the client library or returned to the application. The
135+
client verifies that the named-pipe server is the SCM-registered running
136+
broker before sending any request.
137137
- `VirtualDevice`: common lifecycle for created devices.
138138
- `Gamepad`: submits normalized gamepad state and receives output callbacks.
139139
- `Keyboard`: submits key press/release and UTF-8 text input.
@@ -183,18 +183,18 @@ touch, motion, battery, feedback, and lifecycle updates onto the platform-neutra
183183
184184
Built-in gamepad profiles and their platform-neutral default device names are:
185185
186-
| Profile | Default device name |
187-
| --- | --- |
188-
| Generic HID gamepad | `(libvirtualhid) Generic Controller` |
189-
| Xbox 360 | `(libvirtualhid) X-Box 360 Controller` |
190-
| Xbox One | `(libvirtualhid) X-Box One Controller` |
191-
| Xbox Series | `(libvirtualhid) X-Box Series Controller` |
192-
| DualShock 4 USB and Bluetooth | `(libvirtualhid) PS4 Controller` |
193-
| DualSense USB and Bluetooth | `(libvirtualhid) PS5 Controller` |
194-
| Nintendo Switch Pro | `(libvirtualhid) Nintendo Pro Controller` |
186+
| Profile | Default device name |
187+
|-------------------------------|-------------------------------------------|
188+
| Generic HID gamepad | `(libvirtualhid) Generic Controller` |
189+
| Xbox 360 | `(libvirtualhid) X-Box 360 Controller` |
190+
| Xbox One | `(libvirtualhid) X-Box One Controller` |
191+
| Xbox Series | `(libvirtualhid) X-Box Series Controller` |
192+
| DualShock 4 USB and Bluetooth | `(libvirtualhid) PS4 Controller` |
193+
| DualSense USB and Bluetooth | `(libvirtualhid) PS5 Controller` |
194+
| Nintendo Switch Pro | `(libvirtualhid) Nintendo Pro Controller` |
195195
196196
Consumers may replace `DeviceProfile::name` before creating a gamepad, for
197-
example to prepend an application name while preserving the default controller
197+
example, to prepend an application name while preserving the default controller
198198
identity across platform backends.
199199
200200
The platform-neutral Generic HID descriptor reports the D-pad as buttons 13
@@ -203,8 +203,8 @@ through 16 in the input report. Linux may still route that profile through
203203
standard `ABS_HAT0X` and `ABS_HAT0Y` axes.
204204
205205
Profiles advertise support for features such as rumble, trigger rumble, RGB
206-
LEDs, adaptive triggers, motion sensors, touchpads, battery state, profile
207-
specific buttons, and raw output reports. Consumers should query profile and
206+
LEDs, adaptive triggers, motion sensors, touchpads, battery state,
207+
profile-specific buttons, and raw output reports. Consumers should query profile and
208208
backend capabilities before warning users about unsupported client features.
209209
The `misc1` button represents Share/Capture/Mic Mute-style controls and is
210210
available on the generic, Xbox Series, DualSense, and Switch Pro profiles; Xbox

docs/windows-driver.md

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,21 @@ normal desktop application to use the broker without running as administrator
4343
while keeping broker ownership and privileged device operations in the Windows
4444
service.
4545

46+
Status, current-license validation, activation, replacement, deactivation,
47+
gamepad creation, and owned-device destruction are available to authenticated
48+
local users without elevation. Before sending any request, clients compare the
49+
named-pipe server PID to the SCM-registered, currently running
50+
`libvirtualhid_broker` service. This prevents another local process from
51+
impersonating an unavailable broker and collecting a license key. The service
52+
also requests first ownership of the pipe name and rejects remote clients.
53+
54+
All broker messages are fixed-size and fully validated before use, including
55+
protocol versions, exact byte counts, request types, reserved fields, enums,
56+
array bounds, string terminators, and unused payload bytes. Connection, request,
57+
and response operations use cancellable overlapped I/O with explicit completion
58+
and byte-count checks, so a stopped service or disconnected client cannot leave
59+
an operation using expired stack state.
60+
4661
The backend sends fixed-size C protocol structures to the broker. A create
4762
request identifies the backend's existing control handle; the broker duplicates
4863
that handle from the named-pipe client process and issues `DeviceIoControl` on
@@ -101,7 +116,7 @@ cpack -G WIX -C Release --config .\cmake-build-windows-driver\CPackConfig.cmake
101116

102117
The package defaults to UMDF 2.15, matching the inbox VHF UMDF source driver
103118
while still exposing the framework APIs used by libvirtualhid. The driver links
104-
the MSVC runtime statically so the UMDF host process does not need VC runtime
119+
the MSVC runtime statically, so the UMDF host process does not need VC runtime
105120
DLLs beside the driver.
106121

107122
## Developer Install and Validation
@@ -180,7 +195,8 @@ diagnostics.
180195

181196
On Windows, the UI also shows broker license status. It can activate a license
182197
key, refresh validation, deactivate the current machine, and open
183-
compiled purchase or account-management URLs.
198+
compiled purchase or account-management URLs. License management and normal
199+
virtual-gamepad use do not require elevation.
184200

185201
## Installation Notes
186202

@@ -230,8 +246,12 @@ The broker stores machine-scoped license state in:
230246
C:\ProgramData\libvirtualhid\license.dat
231247
```
232248

233-
The file is protected with Windows DPAPI local-machine scope. GitHub Actions
234-
evaluation timing is stored separately with the same protection in
249+
The file is protected with Windows DPAPI local-machine scope. The state
250+
directory and both state files are owned by LocalSystem and use protected DACLs
251+
that grant full access only to `NT SERVICE\libvirtualhid_broker`, LocalSystem,
252+
and built-in administrators; reparse-point state paths are rejected. GitHub
253+
Actions evaluation timing is
254+
stored separately with the same DPAPI and ACL protection in
235255
`C:\ProgramData\libvirtualhid\github-actions-evaluation.dat`. Broker entitlement
236256
configuration is compiled into the Windows broker and diagnostic UI. Update
237257
`src/platform/windows/shared/lvh_windows_broker_config.hpp` when the Polar
@@ -314,9 +334,9 @@ and must not ship the local pull-request test certificate.
314334

315335
## License
316336

317-
The Windows UMDF driver source and generated Windows driver package artifacts,
318-
including the driver MSI, are licensed under the LizardByte Source-Available
319-
License 1.0 (LB-SAL 1.0). See the [license map](../LICENSES/README.md) for the
320-
full repository license split. The MSI may also include MIT-licensed helper
321-
components from this repository, so packaged installs include both license
322-
texts.
337+
The Windows UMDF driver, broker, proprietary entitlement/evaluation sources,
338+
and generated Windows driver package artifacts, including the driver MSI, are
339+
licensed under the LizardByte Source-Available License 1.0 (LB-SAL 1.0). See
340+
the [license map](../LICENSES/README.md) for the full repository license split.
341+
The MSI may also include MIT-licensed helper components from this repository,
342+
so packaged installs include both license texts.

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ elseif(WIN32)
6262
_WIN32_WINNT=0x0600)
6363
target_link_libraries(${PROJECT_NAME}
6464
PRIVATE
65+
advapi32
6566
setupapi
6667
lizardbyte::common)
6768
elseif(APPLE)

src/include/libvirtualhid/license.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ namespace lvh {
8181
*
8282
* The license key is sent directly to the platform license service. The library does not
8383
* persist a copy or expose it in the returned status.
84+
* On Windows, authenticated local clients can activate or replace a license without elevation.
8485
*
8586
* @param license_key License key supplied by the customer.
8687
* @param instance_name Optional customer-visible name for this machine activation.
@@ -98,6 +99,8 @@ namespace lvh {
9899
/**
99100
* @brief Deactivate the stored license from this machine.
100101
*
102+
* On Windows, authenticated local clients can deactivate a license without elevation.
103+
*
101104
* @return Deactivation result and latest license details.
102105
*/
103106
LicenseResult deactivate_license();

0 commit comments

Comments
 (0)