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.

271 lines
7.7 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
17 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 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 dbus
  18. import sys
  19. import os
  20. from xml.dom import minidom
  21. class Device:
  22. def __init__(self, udi):
  23. self.__udi = udi
  24. self.__findStorageDevice()
  25. deviceObj = bus.get_object('org.freedesktop.Hal',
  26. udi)
  27. deviceProperties = deviceObj.GetAllProperties(
  28. dbus_interface = 'org.freedesktop.Hal.Device')
  29. self.vendor = deviceProperties['usb_device.vendor']
  30. self.product = deviceProperties['info.product']
  31. self.serialNumber = deviceProperties['usb_device.serial']
  32. if len(self.volumes()) < 1:
  33. raise Exception, '%s does not contain any volume' % self.__udi
  34. def __isChildOfDevice(self, udi):
  35. obj = bus.get_object('org.freedesktop.Hal', udi)
  36. properties = obj.GetAllProperties(
  37. dbus_interface = 'org.freedesktop.Hal.Device')
  38. if not properties.has_key('info.parent'):
  39. return False
  40. if properties.has_key('info.bus') and properties['info.bus'] == 'usb_device':
  41. return False
  42. if properties['info.parent'] == self.__udi:
  43. return True
  44. return self.__isChildOfDevice(properties['info.parent'])
  45. def __findStorageDevice(self):
  46. for child in halManager.FindDeviceByCapability('storage'):
  47. if self.__isChildOfDevice(child):
  48. self.__storageUdi = child
  49. return
  50. raise Exception, '%s is not a storage device.' % self.__udi
  51. def __repr__(self):
  52. return "%s %s (%s)" % (self.vendor, self.product, self.serialNumber)
  53. def volumes(self):
  54. vols = []
  55. for volume in halManager.FindDeviceByCapability('volume'):
  56. deviceObj = bus.get_object('org.freedesktop.Hal',
  57. volume)
  58. deviceProperties = deviceObj.GetAllProperties(
  59. dbus_interface = 'org.freedesktop.Hal.Device')
  60. if deviceProperties['block.storage_device'] != self.__storageUdi:
  61. continue
  62. vols.append({'uuid' : deviceProperties['volume.uuid'],
  63. 'device' : deviceProperties['block.device']})
  64. return vols
  65. def listOptions(question, options, autodetect = True):
  66. if autodetect == True and len(options) == 1:
  67. print question
  68. print "* Using \"%s\" (only option)" % options[0]
  69. print
  70. return 0
  71. while True:
  72. try:
  73. print question
  74. for i in range(len(options)):
  75. print "%d) %s" % (i, options[i])
  76. print
  77. sys.stdout.write('[%s-%s]: ' % (0, len(options) - 1))
  78. optionId = int(sys.stdin.readline())
  79. print
  80. if optionId not in range(len(options)):
  81. raise Exception
  82. return optionId
  83. except KeyboardInterrupt: sys.exit()
  84. except Exception: pass
  85. else: break
  86. def writeConf(options, doc):
  87. try:
  88. f = open(options['configFile'], 'w')
  89. doc.writexml(f)
  90. f.close()
  91. except Exception, err:
  92. print 'Unable to save %s: %s' % (options['configFile'], err)
  93. sys.exit(1)
  94. else:
  95. print 'Done.'
  96. def shouldSave(options, items):
  97. print "\n".join(["%s\t\t: %s" % item for item in items])
  98. print
  99. print 'Save to %s ?' % options['configFile']
  100. sys.stdout.write('[Y/n] ')
  101. response = sys.stdin.readline().strip()
  102. if len(response) > 0 and response.lower() != 'y':
  103. sys.exit(1)
  104. def prettifyElement(element):
  105. tmp = minidom.parseString(element.toprettyxml())
  106. return tmp.lastChild
  107. def addUser(options):
  108. try:
  109. doc = minidom.parse(options['configFile'])
  110. except Exception, err:
  111. print 'Unable to read %s: %s' % (options['configFile'], err)
  112. sys.exit(1)
  113. devSection = doc.getElementsByTagName('devices')
  114. if len(devSection) == 0:
  115. print 'Malformed configuration file: No <devices> section found.'
  116. sys.exit(1)
  117. devicesObj = devSection[0].getElementsByTagName('device')
  118. if len(devicesObj) == 0:
  119. print 'No devices found.'
  120. print 'You must add a device (--add-device) before adding users'
  121. sys.exit(1)
  122. devices = []
  123. for device in devicesObj:
  124. devices.append(device.getAttribute('id'))
  125. device = devices[listOptions("Which device would you like to use for authentication ?",
  126. devices)]
  127. shouldSave(options, [
  128. ('User', options['userName']),
  129. ('Device', device)
  130. ])
  131. users = doc.getElementsByTagName('users')
  132. user = doc.createElement('user')
  133. user.attributes['id'] = options['userName']
  134. e = doc.createElement('device')
  135. t = doc.createTextNode(device)
  136. e.appendChild(t)
  137. user.appendChild(e)
  138. users[0].appendChild(prettifyElement(user))
  139. writeConf(options, doc)
  140. def addDevice(options):
  141. devices = []
  142. for udi in halManager.FindDeviceStringMatch('info.bus', 'usb_device'):
  143. try:
  144. devices.append(Device(udi))
  145. except Exception, ex:
  146. pass
  147. if len(devices) == 0:
  148. print 'No devices detected.'
  149. sys.exit()
  150. device = devices[listOptions("Please select the device you wish to add.", devices)]
  151. volumes = device.volumes()
  152. volume = volumes[listOptions("Which volume would you like to use for " \
  153. "storing data ?",
  154. ["%s (UUID: %s)" % (volume['device'],
  155. volume['uuid'] or "<UNDEFINED>")
  156. for volume in volumes]
  157. )]
  158. if volume['uuid'] == '':
  159. print 'WARNING: No UUID detected for device %s. One time pads will be disabled.' % volume['device']
  160. shouldSave(options,[
  161. ('Name', options['deviceName']),
  162. ('Vendor', device.vendor or "Unknown"),
  163. ('Model', device.product or "Unknown"),
  164. ('Serial', device.serialNumber),
  165. ('UUID', volume['uuid'] or "Unknown")
  166. ])
  167. try:
  168. doc = minidom.parse(options['configFile'])
  169. except Exception, err:
  170. print 'Unable to read %s: %s' % (options['configFile'], err)
  171. sys.exit(1)
  172. devs = doc.getElementsByTagName('devices')
  173. dev = doc.createElement('device')
  174. dev.attributes['id'] = options['deviceName']
  175. for name, value in (('vendor', device.vendor),
  176. ('model', device.product),
  177. ('serial', device.serialNumber),
  178. ('volume_uuid', volume['uuid'])):
  179. if value == '':
  180. continue
  181. e = doc.createElement(name)
  182. t = doc.createTextNode(value)
  183. e.appendChild(t)
  184. dev.appendChild(e)
  185. # Disable one time pads if there's no device UUID
  186. if volume['uuid'] == '':
  187. e = doc.createElement('option')
  188. e.setAttribute('name', 'one_time_pad')
  189. e.appendChild(doc.createTextNode('false'))
  190. dev.appendChild(e)
  191. devs[0].appendChild(prettifyElement(dev))
  192. writeConf(options, doc)
  193. def usage():
  194. print 'Usage: %s [--help] [--config=path] [--add-user=name | --add-device=name]' % os.path.basename(__file__)
  195. sys.exit(1)
  196. import getopt
  197. try:
  198. opts, args = getopt.getopt(sys.argv[1:], "hd:nu:c:",
  199. ["help", "add-device=", "add-user=", "config="])
  200. except getopt.GetoptError:
  201. usage()
  202. if len(args) != 0:
  203. usage()
  204. options = { 'deviceName' : None, 'userName' : None,
  205. 'configFile' : '/etc/pamusb.conf' }
  206. for o, a in opts:
  207. if o in ("-h", "--help"):
  208. usage()
  209. if o in ("-d", "--add-device"):
  210. options['deviceName'] = a
  211. if o in ("-u", "--add-user"):
  212. options['userName'] = a
  213. if o in ("-c", "--config"):
  214. options['configFile'] = a
  215. if options['deviceName'] is not None and options['userName'] is not None:
  216. print 'You cannot use both --add-user and --add-device'
  217. usage()
  218. if options['deviceName'] is None and options['userName'] is None:
  219. usage()
  220. if options['deviceName'] is not None:
  221. bus = dbus.SystemBus()
  222. halService = bus.get_object('org.freedesktop.Hal',
  223. '/org/freedesktop/Hal/Manager')
  224. halManager = dbus.Interface(halService, 'org.freedesktop.Hal.Manager')
  225. try:
  226. addDevice(options)
  227. except KeyboardInterrupt:
  228. sys.exit(1)
  229. if options['userName'] is not None:
  230. try:
  231. addUser(options)
  232. except KeyboardInterrupt:
  233. sys.exit(1)