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.

211 lines
5.6 KiB

17 years ago
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2003-2007 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 syslog
  22. import gobject
  23. import dbus
  24. if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
  25. import dbus.glib
  26. try:
  27. import cElementTree as et
  28. except ImportError:
  29. import elementtree.ElementTree as et
  30. class HotPlugDevice:
  31. def __init__(self, serial):
  32. self.__udi = None
  33. self.__serial = serial
  34. self.__callbacks = []
  35. self.__bus = dbus.SystemBus()
  36. self.__running = False
  37. def run(self):
  38. self.__scanDevices()
  39. self.__registerSignals()
  40. self.__running = True
  41. gobject.MainLoop().run()
  42. print 'signals registered'
  43. def addCallback(self, callback):
  44. self.__callbacks.append(callback)
  45. def __scanDevices(self):
  46. halService = self.__bus.get_object('org.freedesktop.Hal',
  47. '/org/freedesktop/Hal/Manager')
  48. halManager = dbus.Interface(halService, 'org.freedesktop.Hal.Manager')
  49. for udi in halManager.FindDeviceStringMatch('info.bus', 'usb_device'):
  50. self.__deviceAdded(udi)
  51. def __registerSignals(self):
  52. for signal, callback in (('DeviceAdded', self.__deviceAdded),
  53. ('DeviceRemoved', self.__deviceRemoved)):
  54. self.__bus.add_signal_receiver(callback,
  55. signal,
  56. 'org.freedesktop.Hal.Manager',
  57. 'org.freedesktop.Hal',
  58. '/org/freedesktop/Hal/Manager')
  59. def __deviceAdded(self, udi):
  60. if self.__udi is not None:
  61. return
  62. deviceObj = self.__bus.get_object('org.freedesktop.Hal',
  63. udi)
  64. deviceProperties = deviceObj.GetAllProperties(
  65. dbus_interface = 'org.freedesktop.Hal.Device')
  66. if not deviceProperties.has_key('usb_device.serial'):
  67. return
  68. if deviceProperties['usb_device.serial'] != self.__serial:
  69. return
  70. self.__udi = udi
  71. if self.__running:
  72. [ cb('added') for cb in self.__callbacks ]
  73. def __deviceRemoved(self, udi):
  74. if self.__udi is None:
  75. return
  76. if self.__udi != udi:
  77. return
  78. self.__udi = None
  79. if self.__running:
  80. [ cb('removed') for cb in self.__callbacks ]
  81. class Log:
  82. def __init__(self):
  83. syslog.openlog('pamusb-agent', syslog.LOG_PID | syslog.LOG_PERROR,
  84. syslog.LOG_AUTH)
  85. def info(self, message):
  86. self.__logMessage(syslog.LOG_NOTICE, message)
  87. def error(self, message):
  88. self.__logMessage(syslog.LOG_ERR, message)
  89. def __logMessage(self, priority, message):
  90. syslog.syslog(priority, message)
  91. def usage():
  92. print 'Usage: %s [--help] [--config=path] [--daemon] [--check=path]' % \
  93. os.path.basename(__file__)
  94. sys.exit(1)
  95. import getopt
  96. try:
  97. opts, args = getopt.getopt(sys.argv[1:], "hc:dc:",
  98. ["help", "config=", "daemon", "check="])
  99. except getopt.GetoptError:
  100. usage()
  101. options = {'configFile' : '/etc/pamusb.conf',
  102. 'daemon' : False,
  103. 'check' : '/usr/bin/pamusb-check'}
  104. if len(args) != 0:
  105. usage()
  106. for o, a in opts:
  107. if o in ('-h', '--help'):
  108. usage()
  109. if o in ('-c', '--config'):
  110. options['configFile'] = a
  111. if o in ('-d', '--daemon'):
  112. options['daemon'] = True
  113. if o in ('-c', '--check'):
  114. options['check'] = a
  115. if not os.path.exists(options['check']):
  116. print '%s not found.' % options['check']
  117. print "You might specify manually pamusb-check's location using --check."
  118. usage()
  119. username = pwd.getpwuid(os.getuid())[0]
  120. logger = Log()
  121. doc = et.parse(options['configFile'])
  122. users = doc.findall('users/user')
  123. for user in users:
  124. if user.get('id') == username:
  125. break
  126. else:
  127. logger.error('User %s not found in configuration file' % username)
  128. sys.exit(1)
  129. events = {
  130. 'lock' : [],
  131. 'unlock' : []
  132. }
  133. for hotplug in user.findall('agent'):
  134. events[hotplug.get('event')].append(hotplug.text)
  135. deviceName = user.find('device').text.strip()
  136. devices = doc.findall("devices/device")
  137. for device in devices:
  138. if device.get('id') == deviceName:
  139. break
  140. else:
  141. logger.error('Device %s not found in configurtion file' % deviceName)
  142. sys.exit(1)
  143. serial = device.find('serial').text.strip()
  144. def authChangeCallback(event):
  145. if event == 'removed':
  146. logger.info('Device "%s" has been removed, ' \
  147. 'locking down user "%s"...' % (deviceName, username))
  148. for cmd in events['lock']:
  149. logger.info('Running "%s"' % cmd)
  150. os.system(cmd)
  151. logger.info('Locked.')
  152. return
  153. logger.info('Device "%s" has been inserted. ' \
  154. 'Performing verification...' % deviceName)
  155. cmdLine = "%s --quiet --config=%s --service=pamusb-agent %s" % (
  156. options['check'], options['configFile'], username)
  157. logger.info('Executing "%s"' % cmdLine)
  158. if not os.system(cmdLine):
  159. logger.info('Authentication succeeded. ' \
  160. 'Unlocking user "%s"...' % username)
  161. for cmd in events['unlock']:
  162. logger.info('Running "%s"' % cmd)
  163. os.system(cmd)
  164. logger.info('Unlocked.')
  165. else:
  166. logger.info('Authentication failed for device %s. ' \
  167. 'Keeping user "%s" locked down.' % (deviceName, username))
  168. hpDev = HotPlugDevice(serial)
  169. hpDev.addCallback(authChangeCallback)
  170. if options['daemon'] and os.fork():
  171. sys.exit(0)
  172. logger.info('pamusb-agent up and running.')
  173. logger.info('Watching device "%s" for user "%s"' % (deviceName, username))
  174. try:
  175. hpDev.run()
  176. except KeyboardInterrupt:
  177. logger.error('Caught keyboard interruption, exiting...')