Hardware authentication for Linux using ordinary USB Flash Drives.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
5.5 KiB

  1. #!/usr/bin/env python2.4
  2. #
  3. # Copyright (c) 2003-2006 Andrea Luzzardi <scox@sig11.org>
  4. #
  5. # This file is part of the pam_usb project. pam_usb is free software;
  6. # you can redistribute it and/or modify it under the terms of the GNU General
  7. # Public License version 2, as published by the Free Software Foundation.
  8. #
  9. # pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY
  10. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  12. # details.
  13. #
  14. # You should have received a copy of the GNU General Public License along with
  15. # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  16. # Place, Suite 330, Boston, MA 02111-1307 USA
  17. import os
  18. import sys
  19. import pwd
  20. import getopt
  21. import gobject
  22. import dbus
  23. if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
  24. import dbus.glib
  25. try:
  26. import cElementTree as et
  27. except ImportError:
  28. import elementtree.ElementTree as et
  29. class HotPlugDevice:
  30. def __init__(self, serial):
  31. self.__udi = None
  32. self.__serial = serial
  33. self.__callbacks = []
  34. self.__bus = dbus.SystemBus()
  35. self.__running = False
  36. def run(self):
  37. self.__scanDevices()
  38. self.__registerSignals()
  39. self.__running = True
  40. gobject.MainLoop().run()
  41. print 'signals registered'
  42. def addCallback(self, callback):
  43. self.__callbacks.append(callback)
  44. def __scanDevices(self):
  45. halService = self.__bus.get_object('org.freedesktop.Hal',
  46. '/org/freedesktop/Hal/Manager')
  47. halManager = dbus.Interface(halService, 'org.freedesktop.Hal.Manager')
  48. for udi in halManager.FindDeviceStringMatch('info.bus', 'usb_device'):
  49. self.__deviceAdded(udi)
  50. def __registerSignals(self):
  51. for signal, callback in (('DeviceAdded', self.__deviceAdded),
  52. ('DeviceRemoved', self.__deviceRemoved)):
  53. self.__bus.add_signal_receiver(callback,
  54. signal,
  55. 'org.freedesktop.Hal.Manager',
  56. 'org.freedesktop.Hal',
  57. '/org/freedesktop/Hal/Manager')
  58. def __deviceAdded(self, udi):
  59. if self.__udi is not None:
  60. return
  61. deviceObj = self.__bus.get_object('org.freedesktop.Hal',
  62. udi)
  63. deviceProperties = deviceObj.GetAllProperties(
  64. dbus_interface = 'org.freedesktop.Hal.Device')
  65. if not deviceProperties.has_key('usb_device.serial'):
  66. return
  67. if deviceProperties['usb_device.serial'] != self.__serial:
  68. return
  69. self.__udi = udi
  70. if self.__running:
  71. [ cb('added') for cb in self.__callbacks ]
  72. def __deviceRemoved(self, udi):
  73. if self.__udi is None:
  74. return
  75. if self.__udi != udi:
  76. return
  77. self.__udi = None
  78. if self.__running:
  79. [ cb('removed') for cb in self.__callbacks ]
  80. def usage():
  81. print 'Usage: %s [--config file] [--daemon] [--path pusb_check_path]' \
  82. % sys.argv[0]
  83. sys.exit(1)
  84. import getopt
  85. try:
  86. opts, args = getopt.getopt(sys.argv[1:], "hc:dp:",
  87. ["help", "config=", "daemon", "path="])
  88. except getopt.GetoptError:
  89. usage()
  90. options = {'configFile' : '/etc/pam_usb/pusb.conf',
  91. 'daemon' : False,
  92. 'path' : '/usr/bin/pusb_check'}
  93. if len(args) != 0:
  94. usage()
  95. for o, a in opts:
  96. if o in ('-h', '--help'):
  97. usage()
  98. if o in ('-c', '--config'):
  99. options['configFile'] = a
  100. if o in ('-d', '--daemon'):
  101. options['daemon'] = True
  102. if o in ('-p', '--path'):
  103. options['path'] = a
  104. if not os.path.exists(options['path']):
  105. print '%s not found.' % options['path']
  106. print "You might specify manually pusb_check's location using --path."
  107. usage()
  108. username = pwd.getpwuid(os.getuid())[0]
  109. print 'Running pusb_hotplug for user %s' % username
  110. doc = et.parse(options['configFile'])
  111. users = doc.findall('users/user')
  112. for user in users:
  113. if user.get('id') == username:
  114. break
  115. else:
  116. print 'User %s not found' % username
  117. events = {
  118. 'lock' : [],
  119. 'unlock' : []
  120. }
  121. for hotplug in user.findall('hotplug'):
  122. events[hotplug.get('event')].append(hotplug.text)
  123. deviceName = user.find('device').text
  124. devices = doc.findall("devices/device")
  125. for device in devices:
  126. if device.get('id') == deviceName:
  127. break
  128. else:
  129. print 'Device %s not found' % deviceName
  130. serial = device.find('serial').text
  131. def authChangeCallback(event):
  132. print 'Device for user %s was %s' % (username, event)
  133. if event == 'removed':
  134. print 'Locking'
  135. [os.system(cmd) for cmd in events['lock'] ]
  136. return
  137. cmdLine = "%s -q -c %s -u %s -s pusb_hotplug -a" % (options['path'],
  138. options['configFile'],
  139. username)
  140. print 'Executing %s' % cmdLine
  141. if not os.system(cmdLine):
  142. print 'Authentication succeeded. Unlocking.'
  143. [os.system(cmd) for cmd in events['unlock'] ]
  144. else:
  145. print 'Authentication failed.'
  146. hpDev = HotPlugDevice(serial)
  147. hpDev.addCallback(authChangeCallback)
  148. if options['daemon'] and os.fork():
  149. sys.exit(0)
  150. try:
  151. hpDev.run()
  152. except KeyboardInterrupt:
  153. print 'Exiting...'