@@ -454,15 +454,22 @@ async def vm_volume_revert(self, untrusted_payload):
454454 )
455455 untrusted_revision = untrusted_payload .decode ("ascii" ).strip ()
456456 del untrusted_payload
457+ allowed_chars = string .ascii_letters + string .digits + "-_."
458+ self .enforce (
459+ all (c in allowed_chars for c in untrusted_revision ),
460+ reason = "Revision name must be in safe set: " + allowed_chars ,
461+ )
462+ revision = untrusted_revision
463+ del untrusted_revision
457464
458465 volume = self .dest .volumes [self .arg ]
466+
467+ self .fire_event_for_permission (volume = volume , revision = revision )
468+
459469 snapshots = volume .revisions
460- # TODO: ben: info-leak: revision existence
461- if untrusted_revision not in snapshots :
470+ if revision not in snapshots :
462471 raise qubes .exc .QubesVolumeRevisionNotFoundError ()
463- revision = untrusted_revision
464472
465- self .fire_event_for_permission (volume = volume , revision = revision )
466473 await qubes .utils .coro_maybe (volume .revert (revision ))
467474 self .app .save ()
468475
@@ -519,10 +526,8 @@ async def vm_volume_clone_to(self, untrusted_payload):
519526 del self .app .api_admin_pending_clone [token ]
520527
521528 # make sure the volume still exists, but invalidate token anyway
522- # TODO: ben: info-leak: pool existence
523529 if str (src_volume .pool ) not in self .app .pools :
524530 raise qubes .exc .QubesPoolNotFoundError ()
525- # TODO: ben: info-leak: volume existence
526531 if src_volume not in self .app .pools [str (src_volume .pool )].volumes :
527532 raise qubes .exc .QubesVolumeNotFoundError ()
528533
@@ -796,9 +801,9 @@ async def pool_usage(self):
796801 @qubes .api .method ("admin.pool.Add" , scope = "global" , write = True )
797802 async def pool_add (self , untrusted_payload ):
798803 self .enforce_dest_dom0 (wants = True )
799- # TODO: ben: info-leak: pool existence
804+ # TODO: ben: info-leak: driver existence: can be done on the policy
800805 self .enforce_arg (
801- wants = qubes .storage .pool_drivers (), short_reason = "available pools "
806+ wants = qubes .storage .pool_drivers (), short_reason = "available drivers "
802807 )
803808
804809 try :
@@ -838,9 +843,9 @@ async def pool_add(self, untrusted_payload):
838843 reason = "Pool name must be in safe set: " + allowed_chars ,
839844 )
840845 pool_name = untrusted_pool_name
841- # TODO: ben: info-leak: pool existence
846+ invalid_pool = False
842847 if pool_name in self .app .pools :
843- raise qubes . exc . QubesPoolInUseError ( pool_name = pool_name )
848+ invalid_pool = True
844849
845850 driver_parameters = qubes .storage .driver_parameters (self .arg )
846851 dp_names = driver_parameters .keys ()
@@ -864,6 +869,9 @@ async def pool_add(self, untrusted_payload):
864869
865870 self .fire_event_for_permission (name = pool_name , pool_config = pool_config )
866871
872+ if invalid_pool :
873+ raise qubes .exc .QubesPoolInUseError (pool_name = pool_name )
874+
867875 await self .app .add_pool (name = pool_name , driver = self .arg , ** pool_config )
868876 self .app .save ()
869877
@@ -955,7 +963,7 @@ async def label_get(self):
955963 self .enforce_dest_dom0 (wants = True )
956964 qubes .utils .validate_label (untrusted_label = self .arg )
957965
958- # TODO: ben: info-leak: label existence
966+ # TODO: ben: info-leak: label existence: can be solved via the policy
959967 label = self .app .get_label (self .arg )
960968
961969 self .fire_event_for_permission (label = label )
@@ -969,7 +977,7 @@ async def label_index(self):
969977 self .enforce_dest_dom0 (wants = True )
970978 qubes .utils .validate_label (untrusted_label = self .arg )
971979
972- # TODO: ben: info-leak: label existence
980+ # TODO: ben: info-leak: label existence: can be solved via the policy
973981 label = self .app .get_label (self .arg )
974982
975983 self .fire_event_for_permission (label = label )
@@ -981,15 +989,6 @@ async def label_create(self, untrusted_payload):
981989 self .enforce_dest_dom0 (wants = True )
982990 qubes .utils .validate_label (untrusted_label = self .arg )
983991
984- # TODO: ben: info-leak: label existence
985- try :
986- self .app .get_label (self .arg )
987- except KeyError :
988- # ok, no such label yet
989- pass
990- else :
991- raise qubes .exc .QubesLabelInUseError (label = self .arg )
992-
993992 qubes .utils .validate_label_value (
994993 untrusted_label_value = untrusted_payload
995994 )
@@ -1000,6 +999,14 @@ async def label_create(self, untrusted_payload):
1000999
10011000 self .fire_event_for_permission (color = color )
10021001
1002+ try :
1003+ self .app .get_label (self .arg )
1004+ except KeyError :
1005+ # ok, no such label yet
1006+ pass
1007+ else :
1008+ raise qubes .exc .QubesLabelInUseError (label = self .arg )
1009+
10031010 # allocate new index, but make sure it's outside of default labels set
10041011 new_index = (
10051012 max (qubes .config .max_default_label , * self .app .labels .keys ()) + 1
@@ -1016,15 +1023,16 @@ async def label_remove(self):
10161023 self .enforce_dest_dom0 (wants = True )
10171024 qubes .utils .validate_label (untrusted_label = self .arg )
10181025
1019- # TODO: ben: info-leak: label existence
1026+ # TODO: ben: info-leak: label existence: can be solved via the policy
10201027 label = self .app .get_label (self .arg )
1028+
1029+ self .fire_event_for_permission (label = label )
1030+
10211031 self .enforce (
10221032 label .index > qubes .config .max_default_label ,
10231033 reason = "Can only remove custom labels" ,
10241034 )
10251035
1026- self .fire_event_for_permission (label = label )
1027-
10281036 # FIXME: this should be in app.add_label()
10291037 for vm in self .app .domains :
10301038 if vm .label == label :
@@ -1315,17 +1323,18 @@ async def _vm_create(
13151323
13161324 # if argument is given, it needs to be a valid template, and only
13171325 # when given VM class do need a template
1326+ invalid_template = False
13181327 if self .arg :
1319- if hasattr (vm_class , "template" ):
1320- # TODO: ben: info-leak: template existence
1321- if self .arg not in self .app .domains :
1322- raise qubes .exc .QubesVMNotFoundError (self .arg )
1323- kwargs ["template" ] = self .app .domains [self .arg ]
1324- else :
1328+ if not hasattr (vm_class , "template" ):
13251329 raise qubes .exc .ProtocolError (
13261330 "{} cannot be based on template" .format (vm_type )
13271331 )
1332+ if self .arg in self .app .domains :
1333+ kwargs ["template" ] = self .app .domains [self .arg ]
1334+ else :
1335+ invalid_template = True
13281336
1337+ invalid_label = False
13291338 for untrusted_param in untrusted_payload .decode (
13301339 "ascii" , errors = "strict"
13311340 ).split (" " ):
@@ -1349,9 +1358,11 @@ async def _vm_create(
13491358 all (c in allowed_chars for c in untrusted_value ),
13501359 reason = "Label must be in safe set: " + allowed_chars ,
13511360 )
1352- # TODO: ben: info-leak: label existence
13531361 qubes .utils .validate_label (untrusted_label = untrusted_value )
1354- kwargs ["label" ] = self .app .get_label (untrusted_value )
1362+ try :
1363+ kwargs ["label" ] = self .app .get_label (untrusted_value )
1364+ except qubes .exc .QubesLabelNotFoundError :
1365+ invalid_label = True
13551366
13561367 elif untrusted_key == "pool" and allow_pool :
13571368 self .enforce (
@@ -1400,13 +1411,27 @@ async def _vm_create(
14001411 reason = "Conflicting options specified: 'pool=', 'pool:volume='" ,
14011412 )
14021413
1403- # TODO: ben: info-leak: qube existence
1414+ invalid_name = False
14041415 if kwargs ["name" ] in self .app .domains :
1416+ invalid_name = True
1417+
1418+ self .fire_event_for_permission (pool = pool , pools = pools , ** kwargs )
1419+
1420+ # Avoids leaking qube existence before admin-permission.
1421+ if invalid_name :
14051422 raise qubes .exc .ProtocolError (
14061423 "Qube already exists: {}" .format (kwargs ["name" ])
14071424 )
14081425
1409- self .fire_event_for_permission (pool = pool , pools = pools , ** kwargs )
1426+ # Avoids leaking template existence before admin-permission.
1427+ if invalid_template :
1428+ raise qubes .exc .QubesVMNotFoundError (self .arg )
1429+
1430+ # Avoids leaking label existence before admin-permission.
1431+ if invalid_label :
1432+ # If label is invalid, it came from payload and should not be
1433+ # logged.
1434+ raise qubes .exc .QubesLabelNotFoundError (label = "untrusted label" )
14101435
14111436 vm = self .app .add_new_vm (vm_class , ** kwargs )
14121437 # TODO: move this to extension (in race-free fashion)
0 commit comments