Skip to content

Commit dbf821c

Browse files
TarekTab-Tarek3600
authored andcommitted
admin: validate feature name in vm_feature_remove
Feature name validation was missing in vm_feature_remove while vm_feature_set already validated it, causing inconsistent behavior between dom0 and management qube. Add shared _validate_feature_name() function to avoid future discrepancies. Use ProtocolError consistent with PR #751. Also validate arg in AbstractQubesAPI.__init__() consistent with sanitize_name() in qrexec-daemon.c. Fixes: QubesOS/qubes-issues#7186 Related: #751
1 parent 53ca30f commit dbf821c

3 files changed

Lines changed: 41 additions & 8 deletions

File tree

qubes/api/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ def __init__(
166166

167167
#: argument
168168
self.arg = arg.decode("ascii")
169+
# Validate arg: must only contain characters allowed by qrexec
170+
# See sanitize_name() in qrexec-daemon.c
171+
if self.arg and not re.match(r"\A[\w.-]+\Z", self.arg):
172+
raise ProtocolError(f"arg not allowed in qrexec: {self.arg!r}")
169173

170174
#: name of the method
171175
self.method = method_name.decode("ascii")
@@ -232,7 +236,7 @@ def fire_event_for_permission(self, **kwargs):
232236
pre_event=True,
233237
dest=self.dest,
234238
arg=self.arg,
235-
**kwargs
239+
**kwargs,
236240
)
237241

238242
def fire_event_for_filter(self, iterable, **kwargs):

qubes/api/admin.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ def on_domain_delete(self, subject, event, vm):
100100
vm.remove_handler("*", self.vm_handler)
101101

102102

103+
def _validate_feature_name(name):
104+
"""Validate feature name against allowed character set.
105+
106+
Allowed characters: alphanumeric, dot, underscore, hyphen.
107+
Raises ProtocolError if name is invalid.
108+
"""
109+
if not re.match(r"\A[a-zA-Z0-9_.-]+\Z", name):
110+
raise qubes.exc.ProtocolError(
111+
"feature name contains illegal characters"
112+
)
113+
114+
103115
class QubesAdminAPI(qubes.api.AbstractQubesAPI):
104116
"""Implementation of Qubes Management API calls
105117
@@ -1162,8 +1174,7 @@ async def vm_feature_checkwithtpladminvm(self):
11621174
"admin.vm.feature.Remove", no_payload=True, scope="local", write=True
11631175
)
11641176
async def vm_feature_remove(self):
1165-
# validation of self.arg done by qrexec-policy is enough
1166-
1177+
_validate_feature_name(self.arg)
11671178
self.fire_event_for_permission()
11681179
try:
11691180
del self.dest.features[self.arg]
@@ -1175,10 +1186,7 @@ async def vm_feature_remove(self):
11751186
async def vm_feature_set(self, untrusted_payload):
11761187
untrusted_value = untrusted_payload.decode("ascii", errors="strict")
11771188
del untrusted_payload
1178-
if re.match(r"\A[a-zA-Z0-9_.-]+\Z", self.arg) is None:
1179-
raise qubes.exc.QubesValueError(
1180-
"feature name contains illegal characters"
1181-
)
1189+
_validate_feature_name(self.arg)
11821190
if re.match(r"\A[\x20-\x7E]*\Z", untrusted_value) is None:
11831191
raise qubes.exc.QubesValueError(
11841192
f"{self.arg} value contains illegal characters"
@@ -2021,7 +2029,7 @@ def _send_stats_single(
20212029
the next iteration)
20222030
"""
20232031

2024-
(info_time, info) = self.app.host.get_vm_stats(
2032+
info_time, info = self.app.host.get_vm_stats(
20252033
info_time, info, only_vm=only_vm
20262034
)
20272035
for vm_id, vm_info in info.items():

qubes/tests/api_admin.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,27 @@ def test_301_feature_remove_none(self):
16941694
)
16951695
self.assertFalse(self.app.save.called)
16961696

1697+
def test_302_feature_remove_invalid_name(self):
1698+
# Invalid feature names must raise ProtocolError before any action
1699+
with self.assertRaises(qubes.exc.ProtocolError):
1700+
self.call_mgmt_func(
1701+
b"admin.vm.feature.Remove",
1702+
b"test-vm1",
1703+
b"../../../root/gotcha",
1704+
)
1705+
self.assertFalse(self.app.save.called)
1706+
1707+
def test_302b_feature_set_invalid_name(self):
1708+
# Invalid feature names must raise ProtocolError in vm_feature_set
1709+
with self.assertRaises(qubes.exc.ProtocolError):
1710+
self.call_mgmt_func(
1711+
b"admin.vm.feature.Set",
1712+
b"test-vm1",
1713+
b"../../../root/gotcha",
1714+
b"some-value",
1715+
)
1716+
self.assertFalse(self.app.save.called)
1717+
16971718
def test_303_feature_prohibited(self):
16981719
del self.app.domains[0].fire_event
16991720
feature = qubes.ext.admin.PROHIBITED_FEATURES[0]

0 commit comments

Comments
 (0)