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.

212 lines
5.7 KiB

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