@@ -105,7 +105,7 @@ def __lt__(self, other):
105
105
if isinstance (other , Device ):
106
106
return (self .backend_domain , self .ident ) < \
107
107
(other .backend_domain , other .ident )
108
- return NotImplemented
108
+ return NotImplemented ()
109
109
110
110
def __repr__ (self ):
111
111
return "[%s]:%s" % (self .backend_domain , self .ident )
@@ -160,9 +160,10 @@ def devclass(self, devclass: str):
160
160
161
161
class DeviceCategory (Enum ):
162
162
"""
163
+ Category of peripheral device.
164
+
163
165
Arbitrarily selected interfaces that are important to users,
164
166
thus deserving special recognition such as a custom icon, etc.
165
-
166
167
"""
167
168
Other = "*******"
168
169
@@ -212,12 +213,16 @@ def from_str(interface_encoding: str) -> 'DeviceCategory':
212
213
213
214
214
215
class DeviceInterface :
216
+ """
217
+ Peripheral device interface wrapper.
218
+ """
219
+
215
220
def __init__ (self , interface_encoding : str , devclass : Optional [str ] = None ):
216
221
ifc_padded = interface_encoding .ljust (6 , '*' )
217
222
if devclass :
218
223
if len (ifc_padded ) > 6 :
219
224
print (
220
- f"interface_encoding is too long "
225
+ f"{ interface_encoding = } is too long "
221
226
f"(is { len (interface_encoding )} , expected max. 6) "
222
227
f"for given { devclass = } " ,
223
228
file = sys .stderr
@@ -229,7 +234,7 @@ def __init__(self, interface_encoding: str, devclass: Optional[str] = None):
229
234
devclass = known_devclasses .get (interface_encoding [0 ], None )
230
235
if len (ifc_padded ) > 7 :
231
236
print (
232
- f"interface_encoding is too long "
237
+ f"{ interface_encoding = } is too long "
233
238
f"(is { len (interface_encoding )} , expected max. 7)" ,
234
239
file = sys .stderr
235
240
)
@@ -258,18 +263,26 @@ def unknown(cls) -> 'DeviceInterface':
258
263
""" Value for unknown device interface. """
259
264
return cls ("?******" )
260
265
261
- @property
262
266
def __repr__ (self ):
263
267
return self ._interface_encoding
264
268
265
- @property
266
269
def __str__ (self ):
267
270
if self .devclass == "block" :
268
271
return "Block device"
269
272
if self .devclass in ("usb" , "pci" ):
270
- self ._load_classes (self .devclass ).get (
271
- self ._interface_encoding [1 :],
272
- f"Unclassified { self .devclass } device" )
273
+ result = self ._load_classes (self .devclass ).get (
274
+ self ._interface_encoding [1 :], None )
275
+ if result is None :
276
+ result = self ._load_classes (self .devclass ).get (
277
+ self ._interface_encoding [1 :- 2 ] + '**' , None )
278
+ if result is None :
279
+ result = self ._load_classes (self .devclass ).get (
280
+ self ._interface_encoding [1 :- 4 ] + '****' , None )
281
+ if result is None :
282
+ result = f"Unclassified { self .devclass } device"
283
+ return result
284
+ if self .devclass == 'mic' :
285
+ return "Microphone"
273
286
return repr (self )
274
287
275
288
@staticmethod
@@ -591,6 +604,27 @@ def _deserialize(
591
604
def frontend_domain (self ):
592
605
return self .data .get ("frontend_domain" , None )
593
606
607
+ @property
608
+ def full_identity (self ) -> str :
609
+ """
610
+ Get user understandable identification of device not related to ports.
611
+
612
+ In addition to the description returns presented interfaces.
613
+ It is used to auto-attach usb devices, so an attacking device needs to
614
+ mimic not only a name, but also interfaces of trusted device (and have
615
+ to be plugged to the same port). For a common user it is all the data
616
+ she uses to recognize the device.
617
+ """
618
+ allowed_chars = string .digits + string .ascii_letters + '-_.'
619
+ description = ""
620
+ for char in self .description :
621
+ if char in allowed_chars :
622
+ description += char
623
+ else :
624
+ description += "_"
625
+ interfaces = '' .join (repr (ifc ) for ifc in self .interfaces )
626
+ return f'{ description } :{ interfaces } '
627
+
594
628
595
629
def serialize_str (value : str ):
596
630
return repr (str (value ))
@@ -771,7 +805,8 @@ def _deserialize(
771
805
allowed_chars_key = string .digits + string .ascii_letters + '-_.'
772
806
allowed_chars_value = allowed_chars_key + ',+:'
773
807
774
- untrusted_decoded = untrusted_serialization .decode ('ascii' , 'strict' )
808
+ untrusted_decoded = untrusted_serialization .decode (
809
+ 'ascii' , 'strict' ).strip ()
775
810
keys = []
776
811
values = []
777
812
untrusted_key , _ , untrusted_rest = untrusted_decoded .partition ("='" )
@@ -808,7 +843,7 @@ def _deserialize(
808
843
809
844
if properties ['backend_domain' ] != expected_backend_domain .name :
810
845
raise UnexpectedDeviceProperty (
811
- f"Got device exposed by { properties ['backend_domain' ]} "
846
+ f"Got device exposed by { properties ['backend_domain' ]} "
812
847
f"when expected devices from { expected_backend_domain .name } ." )
813
848
properties ['backend_domain' ] = expected_backend_domain
814
849
@@ -895,31 +930,31 @@ def detach(self, device_assignment: DeviceAssignment) -> None:
895
930
device_assignment .backend_domain ,
896
931
device_assignment .ident ))
897
932
898
- def assign (self , device_assignment : DeviceAssignment ) -> None :
933
+ def assign (self , assignment : DeviceAssignment ) -> None :
899
934
"""
900
935
Assign device to domain (add to :file:`qubes.xml`).
901
936
902
- :param DeviceAssignment device_assignment : device object
937
+ :param DeviceAssignment assignment : device object
903
938
"""
904
939
905
- if not device_assignment .frontend_domain :
906
- device_assignment .frontend_domain = self ._vm
940
+ if not assignment .frontend_domain :
941
+ assignment .frontend_domain = self ._vm
907
942
else :
908
- assert device_assignment .frontend_domain == self ._vm , \
943
+ assert assignment .frontend_domain == self ._vm , \
909
944
"Trying to assign DeviceAssignment belonging to other domain"
910
- if not device_assignment .devclass_is_set :
911
- device_assignment .devclass = self ._class
912
- elif device_assignment .devclass != self ._class :
913
- raise ValueError (
945
+ if not assignment .devclass_is_set :
946
+ assignment .devclass = self ._class
947
+ elif assignment .devclass != self ._class :
948
+ raise qubesadmin . ext . QubesValueError (
914
949
f"Device assignment class does not match to expected: "
915
- f"{ device_assignment .devclass = } !={ self ._class = } " )
950
+ f"{ assignment .devclass = } !={ self ._class = } " )
916
951
917
952
self ._vm .qubesd_call (None ,
918
953
'admin.vm.device.{}.Assign' .format (self ._class ),
919
954
'{!s}+{!s}' .format (
920
- device_assignment .backend_domain ,
921
- device_assignment .ident ),
922
- device_assignment .serialize ())
955
+ assignment .backend_domain ,
956
+ assignment .ident ),
957
+ assignment .serialize ())
923
958
924
959
def unassign (self , device_assignment : DeviceAssignment ) -> None :
925
960
"""
0 commit comments