ticket47900_test.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. import os
  2. import sys
  3. import time
  4. import ldap
  5. import logging
  6. import socket
  7. import pytest
  8. from lib389 import DirSrv, Entry, tools
  9. from lib389.tools import DirSrvTools
  10. from lib389._constants import *
  11. from lib389.properties import *
  12. from constants import *
  13. log = logging.getLogger(__name__)
  14. installation_prefix = None
  15. CONFIG_DN = 'cn=config'
  16. ADMIN_NAME = 'passwd_admin'
  17. ADMIN_DN = 'cn=%s,%s' % (ADMIN_NAME, SUFFIX)
  18. ADMIN_PWD = 'adminPassword_1'
  19. ENTRY_NAME = 'Joe Schmo'
  20. ENTRY_DN = 'cn=%s,%s' % (ENTRY_NAME, SUFFIX)
  21. INVALID_PWDS = ('2_Short', 'No_Number', 'N0Special', '{SSHA}bBy8UdtPZwu8uZna9QOYG3Pr41RpIRVDl8wddw==')
  22. class TopologyStandalone(object):
  23. def __init__(self, standalone):
  24. standalone.open()
  25. self.standalone = standalone
  26. @pytest.fixture(scope="module")
  27. def topology(request):
  28. '''
  29. This fixture is used to standalone topology for the 'module'.
  30. At the beginning, It may exists a standalone instance.
  31. It may also exists a backup for the standalone instance.
  32. Principle:
  33. If standalone instance exists:
  34. restart it
  35. If backup of standalone exists:
  36. create/rebind to standalone
  37. restore standalone instance from backup
  38. else:
  39. Cleanup everything
  40. remove instance
  41. remove backup
  42. Create instance
  43. Create backup
  44. '''
  45. global installation_prefix
  46. if installation_prefix:
  47. args_instance[SER_DEPLOYED_DIR] = installation_prefix
  48. standalone = DirSrv(verbose=False)
  49. # Args for the standalone instance
  50. args_instance[SER_HOST] = HOST_STANDALONE
  51. args_instance[SER_PORT] = PORT_STANDALONE
  52. args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
  53. args_standalone = args_instance.copy()
  54. standalone.allocate(args_standalone)
  55. # Get the status of the backups
  56. backup_standalone = standalone.checkBackupFS()
  57. # Get the status of the instance and restart it if it exists
  58. instance_standalone = standalone.exists()
  59. if instance_standalone:
  60. # assuming the instance is already stopped, just wait 5 sec max
  61. standalone.stop(timeout=5)
  62. standalone.start(timeout=10)
  63. if backup_standalone:
  64. # The backup exist, assuming it is correct
  65. # we just re-init the instance with it
  66. if not instance_standalone:
  67. standalone.create()
  68. # Used to retrieve configuration information (dbdir, confdir...)
  69. standalone.open()
  70. # restore standalone instance from backup
  71. standalone.stop(timeout=10)
  72. standalone.restoreFS(backup_standalone)
  73. standalone.start(timeout=10)
  74. else:
  75. # We should be here only in two conditions
  76. # - This is the first time a test involve standalone instance
  77. # - Something weird happened (instance/backup destroyed)
  78. # so we discard everything and recreate all
  79. # Remove the backup. So even if we have a specific backup file
  80. # (e.g backup_standalone) we clear backup that an instance may have created
  81. if backup_standalone:
  82. standalone.clearBackupFS()
  83. # Remove the instance
  84. if instance_standalone:
  85. standalone.delete()
  86. # Create the instance
  87. standalone.create()
  88. # Used to retrieve configuration information (dbdir, confdir...)
  89. standalone.open()
  90. # Time to create the backups
  91. standalone.stop(timeout=10)
  92. standalone.backupfile = standalone.backupFS()
  93. standalone.start(timeout=10)
  94. # clear the tmp directory
  95. standalone.clearTmpDir(__file__)
  96. #
  97. # Here we have standalone instance up and running
  98. # Either coming from a backup recovery
  99. # or from a fresh (re)init
  100. # Time to return the topology
  101. return TopologyStandalone(standalone)
  102. def test_ticket47900(topology):
  103. """
  104. Test that password administrators/root DN can
  105. bypass password syntax/policy.
  106. We need to test how passwords are modified in
  107. existing entries, and when adding new entries.
  108. Create the Password Admin entry, but do not set
  109. it as an admin yet. Use the entry to verify invalid
  110. passwords are caught. Then activate the password
  111. admin and make sure it can bypass password policy.
  112. """
  113. # Prepare the Password Administator
  114. entry = Entry(ADMIN_DN)
  115. entry.setValues('objectclass', 'top', 'person')
  116. entry.setValues('sn', ADMIN_NAME)
  117. entry.setValues('cn', ADMIN_NAME)
  118. entry.setValues('userpassword', ADMIN_PWD)
  119. topology.standalone.log.info("Creating Password Administator entry %s..." % ADMIN_DN)
  120. try:
  121. topology.standalone.add_s(entry)
  122. except ldap.LDAPError, e:
  123. topology.standalone.log.error('Unexpected result ' + e.message['desc'])
  124. assert False
  125. topology.standalone.log.error("Failed to add Password Administator %s, error: %s "
  126. % (ADMIN_DN, e.message['desc']))
  127. assert False
  128. topology.standalone.log.info("Configuring password policy...")
  129. try:
  130. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local' , 'on'),
  131. (ldap.MOD_REPLACE, 'passwordCheckSyntax', 'on'),
  132. (ldap.MOD_REPLACE, 'passwordMinCategories' , '1'),
  133. (ldap.MOD_REPLACE, 'passwordMinTokenLength' , '1'),
  134. (ldap.MOD_REPLACE, 'passwordExp' , 'on'),
  135. (ldap.MOD_REPLACE, 'passwordMinDigits' , '1'),
  136. (ldap.MOD_REPLACE, 'passwordMinSpecials' , '1')])
  137. except ldap.LDAPError, e:
  138. topology.standalone.log.error('Failed configure password policy: ' + e.message['desc'])
  139. assert False
  140. #
  141. # Add an aci to allow everyone all access (just makes things easier)
  142. #
  143. topology.standalone.log.info("Add aci to allow password admin to add/update entries...")
  144. ACI_TARGET = "(target = \"ldap:///%s\")" % SUFFIX
  145. ACI_TARGETATTR = "(targetattr = *)"
  146. ACI_ALLOW = "(version 3.0; acl \"Password Admin Access\"; allow (all) "
  147. ACI_SUBJECT = "(userdn = \"ldap:///anyone\");)"
  148. ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_ALLOW + ACI_SUBJECT
  149. mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
  150. try:
  151. topology.standalone.modify_s(SUFFIX, mod)
  152. except ldap.LDAPError, e:
  153. topology.standalone.log.error('Failed to add aci for password admin: ' + e.message['desc'])
  154. assert False
  155. #
  156. # Bind as the Password Admin
  157. #
  158. topology.standalone.log.info("Bind as the Password Administator (before activating)...")
  159. try:
  160. topology.standalone.simple_bind_s(ADMIN_DN, ADMIN_PWD)
  161. except ldap.LDAPError, e:
  162. topology.standalone.log.error('Failed to bind as the Password Admin: ' + e.message['desc'])
  163. assert False
  164. #
  165. # Setup our test entry, and test password policy is working
  166. #
  167. entry = Entry(ENTRY_DN)
  168. entry.setValues('objectclass', 'top', 'person')
  169. entry.setValues('sn', ENTRY_NAME)
  170. entry.setValues('cn', ENTRY_NAME)
  171. #
  172. # Start by attempting to add an entry with an invalid password
  173. #
  174. topology.standalone.log.info("Attempt to add entries with invalid passwords, these adds should fail...")
  175. for passwd in INVALID_PWDS:
  176. failed_as_expected = False
  177. entry.setValues('userpassword', passwd)
  178. topology.standalone.log.info("Create a regular user entry %s with password (%s)..." % (ENTRY_DN, passwd))
  179. try:
  180. topology.standalone.add_s(entry)
  181. except ldap.LDAPError, e:
  182. # We failed as expected
  183. failed_as_expected = True
  184. topology.standalone.log.info('Add failed as expected: password (%s) result (%s)'
  185. % (passwd, e.message['desc']))
  186. if not failed_as_expected:
  187. topology.standalone.log.error("We were incorrectly able to add an entry " +
  188. "with an invalid password (%s)" % (passwd))
  189. assert False
  190. #
  191. # Now activate a password administator, bind as root dn to do the config
  192. # update, then rebind as the password admin
  193. #
  194. topology.standalone.log.info("Activate the Password Administator...")
  195. # Bind as Root DN
  196. try:
  197. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  198. except ldap.LDAPError, e:
  199. topology.standalone.log.error('Root DN failed to authenticate: ' + e.message['desc'])
  200. assert False
  201. # Update config
  202. try:
  203. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'passwordAdminDN', ADMIN_DN)])
  204. except ldap.LDAPError, e:
  205. topology.standalone.log.error('Failed to add password admin to config: ' + e.message['desc'])
  206. assert False
  207. # Bind as Password Admin
  208. try:
  209. topology.standalone.simple_bind_s(ADMIN_DN, ADMIN_PWD)
  210. except ldap.LDAPError, e:
  211. topology.standalone.log.error('Failed to bind as the Password Admin: ' + e.message['desc'])
  212. assert False
  213. #
  214. # Start adding entries with invalid passwords, delete the entry after each pass.
  215. #
  216. for passwd in INVALID_PWDS:
  217. entry.setValues('userpassword', passwd)
  218. topology.standalone.log.info("Create a regular user entry %s with password (%s)..." % (ENTRY_DN, passwd))
  219. try:
  220. topology.standalone.add_s(entry)
  221. except ldap.LDAPError, e:
  222. topology.standalone.log.error('Failed to add entry with password (%s) result (%s)'
  223. % (passwd, e.message['desc']))
  224. assert False
  225. topology.standalone.log.info('Succesfully added entry (%s)' % ENTRY_DN)
  226. # Delete entry for the next pass
  227. try:
  228. topology.standalone.delete_s(ENTRY_DN)
  229. except ldap.LDAPError, e:
  230. topology.standalone.log.error('Failed to delete entry: %s' % (e.message['desc']))
  231. assert False
  232. #
  233. # Add the entry for the next round of testing (modify password)
  234. #
  235. entry.setValues('userpassword', ADMIN_PWD)
  236. try:
  237. topology.standalone.add_s(entry)
  238. except ldap.LDAPError, e:
  239. topology.standalone.log.error('Failed to add entry with valid password (%s) result (%s)'
  240. % (passwd, e.message['desc']))
  241. assert False
  242. #
  243. # Deactivate the password admin and make sure invalid password updates fail
  244. #
  245. topology.standalone.log.info("Deactivate Password Administator and try invalid password updates...")
  246. # Bind as root DN
  247. try:
  248. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  249. except ldap.LDAPError, e:
  250. topology.standalone.log.error('Root DN failed to authenticate: ' + e.message['desc'])
  251. assert False
  252. # Update config
  253. try:
  254. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_DELETE, 'passwordAdminDN', None)])
  255. except ldap.LDAPError, e:
  256. topology.standalone.log.error('Failed to remove password admin from config: ' + e.message['desc'])
  257. assert False
  258. # Bind as Password Admin
  259. try:
  260. topology.standalone.simple_bind_s(ADMIN_DN, ADMIN_PWD)
  261. except ldap.LDAPError, e:
  262. topology.standalone.log.error('Failed to bind as the Password Admin: ' + e.message['desc'])
  263. assert False
  264. #
  265. # Make invalid password updates that should fail
  266. #
  267. for passwd in INVALID_PWDS:
  268. failed_as_expected = False
  269. entry.setValues('userpassword', passwd)
  270. try:
  271. topology.standalone.modify_s(ENTRY_DN, [(ldap.MOD_REPLACE, 'userpassword', passwd)])
  272. except ldap.LDAPError, e:
  273. # We failed as expected
  274. failed_as_expected = True
  275. topology.standalone.log.info('Password update failed as expected: password (%s) result (%s)'
  276. % (passwd, e.message['desc']))
  277. if not failed_as_expected:
  278. topology.standalone.log.error("We were incorrectly able to add an invalid password (%s)"
  279. % (passwd))
  280. assert False
  281. #
  282. # Now activate a password administator
  283. #
  284. topology.standalone.log.info("Activate Password Administator and try updates again...")
  285. # Bind as root DN
  286. try:
  287. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  288. except ldap.LDAPError, e:
  289. topology.standalone.log.error('Root DN failed to authenticate: ' + e.message['desc'])
  290. assert False
  291. # Update config
  292. try:
  293. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'passwordAdminDN', ADMIN_DN)])
  294. except ldap.LDAPError, e:
  295. topology.standalone.log.error('Failed to add password admin to config: ' + e.message['desc'])
  296. assert False
  297. # Bind as Password Admin
  298. try:
  299. topology.standalone.simple_bind_s(ADMIN_DN, ADMIN_PWD)
  300. except ldap.LDAPError, e:
  301. topology.standalone.log.error('Failed to bind as the Password Admin: ' + e.message['desc'])
  302. assert False
  303. #
  304. # Make the same password updates, but this time they should succeed
  305. #
  306. for passwd in INVALID_PWDS:
  307. entry.setValues('userpassword', passwd)
  308. try:
  309. topology.standalone.modify_s(ENTRY_DN, [(ldap.MOD_REPLACE, 'userpassword', passwd)])
  310. except ldap.LDAPError, e:
  311. topology.standalone.log.error('Password update failed unexpectedly: password (%s) result (%s)'
  312. % (passwd, e.message['desc']))
  313. assert False
  314. topology.standalone.log.info('Password update succeeded (%s)' % passwd)
  315. #
  316. # Test passed
  317. #
  318. topology.standalone.log.info('Test 47900 Passed.')
  319. def test_ticket47900_final(topology):
  320. topology.standalone.delete()
  321. def run_isolated():
  322. '''
  323. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  324. To run isolated without py.test, you need to
  325. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  326. - set the installation prefix
  327. - run this program
  328. '''
  329. global installation_prefix
  330. installation_prefix = None
  331. topo = topology(True)
  332. test_ticket47900(topo)
  333. test_ticket47900_final(topo)
  334. if __name__ == '__main__':
  335. run_isolated()