ticket47900_test.py 12 KB

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