ticket47900_test.py 12 KB

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