usbguard-menu.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #!/usr/bin/env python
  2. from enum import Enum
  3. import subprocess
  4. import shlex
  5. class DeviceMode(Enum):
  6. BLOCK = 0
  7. ALLOW = 1
  8. def __str__(self):
  9. match self:
  10. case DeviceMode.ALLOW:
  11. return 'Allow'
  12. case DeviceMode.BLOCK:
  13. return 'Block'
  14. class Device:
  15. def __init__(self, mode, id, name, hash):
  16. self.mode = mode
  17. self.id = id
  18. self.name = name
  19. self.hash = hash
  20. def from_output_line(line):
  21. tokens = shlex.split(line, False, True)
  22. mode = DeviceMode.BLOCK
  23. id = None
  24. name = None
  25. hash = None
  26. for i, token in enumerate(tokens):
  27. if i == len(tokens) - 1:
  28. next = None
  29. else:
  30. next = tokens[i + 1]
  31. match token:
  32. case 'allow':
  33. mode = DeviceMode.ALLOW
  34. case 'id':
  35. id = next
  36. case 'name':
  37. name = next
  38. case 'hash':
  39. hash = next
  40. return Device(mode, id, name, hash)
  41. def __str__(self):
  42. match self.mode:
  43. case DeviceMode.ALLOW:
  44. mode = "Allowed"
  45. case _:
  46. mode = "Blocked"
  47. if len(self.name) == 0:
  48. name = "<no name>"
  49. else:
  50. name = self.name
  51. return f'{self.id}: {name} ({mode}, Hash: {self.hash})'
  52. def run_fuzzel(entries, prompt=None):
  53. cmd = ["fuzzel", "-d", "--index"]
  54. if prompt is not None:
  55. cmd.append(f'--prompt={prompt}')
  56. proc = subprocess.run(cmd,
  57. input='\n'.join(str(e) for e in entries),
  58. capture_output=True,
  59. text=True,
  60. shell=False)
  61. if proc.returncode != 0:
  62. return (-1, None)
  63. index = int(proc.stdout)
  64. return (index, entries[index])
  65. def get_implicit_policy_target():
  66. proc = subprocess.run(['usbguard',
  67. 'get-parameter',
  68. 'ImplicitPolicyTarget'],
  69. capture_output=True,
  70. text=True,
  71. check=True)
  72. match proc.stdout:
  73. case 'allow\n':
  74. return DeviceMode.ALLOW
  75. case 'block\n':
  76. return DeviceMode.BLOCK
  77. def set_implicit_policy_target(target):
  78. match target:
  79. case DeviceMode.ALLOW:
  80. mode = 'allow'
  81. case DeviceMode.BLOCK:
  82. mode = 'block'
  83. subprocess.run(['usbguard',
  84. 'set-parameter',
  85. 'ImplicitPolicyTarget',
  86. mode],
  87. check=True)
  88. def update_default_action():
  89. (index, _) = run_fuzzel(['Allow', 'Block'],
  90. 'Default action > ')
  91. match index:
  92. case 0:
  93. set_implicit_policy_target(DeviceMode.ALLOW)
  94. case 1:
  95. set_implicit_policy_target(DeviceMode.BLOCK)
  96. def update_device_mode(device):
  97. (_, action) = run_fuzzel(['Allow', 'Block', 'Reject'],
  98. device.name + ' > ')
  99. if action is None:
  100. exit()
  101. match action:
  102. case 'Allow':
  103. subprocess.check_output(['usbguard',
  104. 'allow-device',
  105. device.id], shell=False)
  106. case 'Reject':
  107. subprocess.check_output(['usbguard',
  108. 'reject-device',
  109. device.id], shell=False)
  110. case _:
  111. subprocess.check_output(['usbguard',
  112. 'block-device',
  113. device.id], shell=False)
  114. def main():
  115. devices = []
  116. proc = subprocess.run(["usbguard", "list-devices"],
  117. capture_output=True, text=True, check=True)
  118. for line in proc.stdout.splitlines():
  119. devices.append(Device.from_output_line(line))
  120. devices.append(f'Set default action (Current: {get_implicit_policy_target()})')
  121. (index, device) = run_fuzzel(devices)
  122. if index == len(devices) - 1:
  123. update_default_action()
  124. elif index != -1:
  125. update_device_mode(device)
  126. main()