ticket1347760_test.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2016 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. from subprocess import Popen
  10. import pytest
  11. from lib389.paths import Paths
  12. from lib389.tasks import *
  13. from lib389.utils import *
  14. from lib389.topologies import topology_st
  15. logging.getLogger(__name__).setLevel(logging.DEBUG)
  16. log = logging.getLogger(__name__)
  17. CONFIG_DN = 'cn=config'
  18. BOU = 'BOU'
  19. BINDOU = 'ou=%s,%s' % (BOU, DEFAULT_SUFFIX)
  20. BUID = 'buser123'
  21. TUID = 'tuser0'
  22. BINDDN = 'uid=%s,%s' % (BUID, BINDOU)
  23. BINDPW = BUID
  24. TESTDN = 'uid=%s,ou=people,%s' % (TUID, DEFAULT_SUFFIX)
  25. TESTPW = TUID
  26. BOGUSDN = 'uid=bogus,%s' % DEFAULT_SUFFIX
  27. BOGUSDN2 = 'uid=bogus,ou=people,%s' % DEFAULT_SUFFIX
  28. BOGUSSUFFIX = 'uid=bogus,ou=people,dc=bogus'
  29. GROUPOU = 'ou=groups,%s' % DEFAULT_SUFFIX
  30. BOGUSOU = 'ou=OU,%s' % DEFAULT_SUFFIX
  31. def pattern_accesslog(file, log_pattern):
  32. for i in range(5):
  33. try:
  34. pattern_accesslog.last_pos += 1
  35. except AttributeError:
  36. pattern_accesslog.last_pos = 0
  37. found = None
  38. file.seek(pattern_accesslog.last_pos)
  39. # Use a while true iteration because 'for line in file: hit a
  40. # python bug that break file.tell()
  41. while True:
  42. line = file.readline()
  43. found = log_pattern.search(line)
  44. if ((line == '') or (found)):
  45. break
  46. pattern_accesslog.last_pos = file.tell()
  47. if found:
  48. return line
  49. else:
  50. time.sleep(1)
  51. return None
  52. def check_op_result(server, op, dn, superior, exists, rc):
  53. targetdn = dn
  54. if op == 'search':
  55. if exists:
  56. opstr = 'Searching existing entry'
  57. else:
  58. opstr = 'Searching non-existing entry'
  59. elif op == 'add':
  60. if exists:
  61. opstr = 'Adding existing entry'
  62. else:
  63. opstr = 'Adding non-existing entry'
  64. elif op == 'modify':
  65. if exists:
  66. opstr = 'Modifying existing entry'
  67. else:
  68. opstr = 'Modifying non-existing entry'
  69. elif op == 'modrdn':
  70. if superior != None:
  71. targetdn = superior
  72. if exists:
  73. opstr = 'Moving to existing superior'
  74. else:
  75. opstr = 'Moving to non-existing superior'
  76. else:
  77. if exists:
  78. opstr = 'Renaming existing entry'
  79. else:
  80. opstr = 'Renaming non-existing entry'
  81. elif op == 'delete':
  82. if exists:
  83. opstr = 'Deleting existing entry'
  84. else:
  85. opstr = 'Deleting non-existing entry'
  86. if ldap.SUCCESS == rc:
  87. expstr = 'be ok'
  88. else:
  89. expstr = 'fail with %s' % rc.__name__
  90. log.info('%s %s, which should %s.' % (opstr, targetdn, expstr))
  91. time.sleep(1)
  92. hit = 0
  93. try:
  94. if op == 'search':
  95. centry = server.search_s(dn, ldap.SCOPE_BASE, 'objectclass=*')
  96. elif op == 'add':
  97. server.add_s(Entry((dn, {'objectclass': 'top extensibleObject'.split(),
  98. 'cn': 'test entry'})))
  99. elif op == 'modify':
  100. server.modify_s(dn, [(ldap.MOD_REPLACE, 'description', 'test')])
  101. elif op == 'modrdn':
  102. if superior != None:
  103. server.rename_s(dn, 'uid=new', newsuperior=superior, delold=1)
  104. else:
  105. server.rename_s(dn, 'uid=new', delold=1)
  106. elif op == 'delete':
  107. server.delete_s(dn)
  108. else:
  109. log.fatal('Unknown operation %s' % op)
  110. assert False
  111. except ldap.LDAPError as e:
  112. hit = 1
  113. log.info("Exception (expected): %s" % type(e).__name__)
  114. log.info('Desc ' + e.message['desc'])
  115. assert isinstance(e, rc)
  116. if e.message.has_key('matched'):
  117. log.info('Matched is returned: ' + e.message['matched'])
  118. if rc != ldap.NO_SUCH_OBJECT:
  119. assert False
  120. if ldap.SUCCESS == rc:
  121. if op == 'search':
  122. log.info('Search should return none')
  123. assert len(centry) == 0
  124. else:
  125. if 0 == hit:
  126. log.info('Expected to fail with %s, but passed' % rc.__name__)
  127. assert False
  128. log.info('PASSED\n')
  129. def test_ticket1347760(topology_st):
  130. """
  131. Prevent revealing the entry info to whom has no access rights.
  132. """
  133. log.info('Testing Bug 1347760 - Information disclosure via repeated use of LDAP ADD operation, etc.')
  134. log.info('Disabling accesslog logbuffering')
  135. topology_st.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-accesslog-logbuffering', 'off')])
  136. log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
  137. topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
  138. log.info('Adding ou=%s a bind user belongs to.' % BOU)
  139. topology_st.standalone.add_s(Entry((BINDOU, {
  140. 'objectclass': 'top organizationalunit'.split(),
  141. 'ou': BOU})))
  142. log.info('Adding a bind user.')
  143. topology_st.standalone.add_s(Entry((BINDDN,
  144. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  145. 'cn': 'bind user',
  146. 'sn': 'user',
  147. 'userPassword': BINDPW})))
  148. log.info('Adding a test user.')
  149. topology_st.standalone.add_s(Entry((TESTDN,
  150. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  151. 'cn': 'test user',
  152. 'sn': 'user',
  153. 'userPassword': TESTPW})))
  154. log.info('Deleting aci in %s.' % DEFAULT_SUFFIX)
  155. topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_DELETE, 'aci', None)])
  156. log.info('While binding as DM, acquire an access log path')
  157. ds_paths = Paths(serverid=topology_st.standalone.serverid,
  158. instance=topology_st.standalone)
  159. file_path = ds_paths.access_log
  160. log.info('Bind case 1. the bind user has no rights to read the entry itself, bind should be successful.')
  161. log.info('Bind as {%s,%s} who has no access rights.' % (BINDDN, BINDPW))
  162. try:
  163. topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
  164. except ldap.LDAPError as e:
  165. log.info('Desc ' + e.message['desc'])
  166. assert False
  167. file_obj = open(file_path, "r")
  168. log.info('Access log path: %s' % file_path)
  169. log.info(
  170. 'Bind case 2-1. the bind user does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
  171. log.info('Bind as {%s,%s} who does not exist.' % (BOGUSDN, 'bogus'))
  172. try:
  173. topology_st.standalone.simple_bind_s(BOGUSDN, 'bogus')
  174. except ldap.LDAPError as e:
  175. log.info("Exception (expected): %s" % type(e).__name__)
  176. log.info('Desc ' + e.message['desc'])
  177. assert isinstance(e, ldap.INVALID_CREDENTIALS)
  178. regex = re.compile('No such entry')
  179. cause = pattern_accesslog(file_obj, regex)
  180. if cause == None:
  181. log.fatal('Cause not found - %s' % cause)
  182. assert False
  183. else:
  184. log.info('Cause found - %s' % cause)
  185. time.sleep(1)
  186. log.info(
  187. 'Bind case 2-2. the bind user\'s suffix does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
  188. log.info('Bind as {%s,%s} who does not exist.' % (BOGUSSUFFIX, 'bogus'))
  189. try:
  190. topology_st.standalone.simple_bind_s(BOGUSSUFFIX, 'bogus')
  191. except ldap.LDAPError as e:
  192. log.info("Exception (expected): %s" % type(e).__name__)
  193. log.info('Desc ' + e.message['desc'])
  194. assert isinstance(e, ldap.INVALID_CREDENTIALS)
  195. regex = re.compile('No such suffix')
  196. cause = pattern_accesslog(file_obj, regex)
  197. if cause == None:
  198. log.fatal('Cause not found - %s' % cause)
  199. assert False
  200. else:
  201. log.info('Cause found - %s' % cause)
  202. time.sleep(1)
  203. log.info(
  204. 'Bind case 2-3. the bind user\'s password is wrong, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
  205. log.info('Bind as {%s,%s} who does not exist.' % (BINDDN, 'bogus'))
  206. try:
  207. topology_st.standalone.simple_bind_s(BINDDN, 'bogus')
  208. except ldap.LDAPError as e:
  209. log.info("Exception (expected): %s" % type(e).__name__)
  210. log.info('Desc ' + e.message['desc'])
  211. assert isinstance(e, ldap.INVALID_CREDENTIALS)
  212. regex = re.compile('Invalid credentials')
  213. cause = pattern_accesslog(file_obj, regex)
  214. if cause == None:
  215. log.fatal('Cause not found - %s' % cause)
  216. assert False
  217. else:
  218. log.info('Cause found - %s' % cause)
  219. time.sleep(1)
  220. log.info('Adding aci for %s to %s.' % (BINDDN, BINDOU))
  221. acival = '(targetattr="*")(version 3.0; acl "%s"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN)
  222. log.info('aci: %s' % acival)
  223. log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
  224. topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
  225. topology_st.standalone.modify_s(BINDOU, [(ldap.MOD_ADD, 'aci', acival)])
  226. time.sleep(1)
  227. log.info('Bind case 3. the bind user has the right to read the entry itself, bind should be successful.')
  228. log.info('Bind as {%s,%s} which should be ok.\n' % (BINDDN, BINDPW))
  229. topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
  230. log.info('The following operations are against the subtree the bind user %s has no rights.' % BINDDN)
  231. # Search
  232. exists = True
  233. rc = ldap.SUCCESS
  234. log.info(
  235. 'Search case 1. the bind user has no rights to read the search entry, it should return no search results with %s' % rc)
  236. check_op_result(topology_st.standalone, 'search', TESTDN, None, exists, rc)
  237. exists = False
  238. rc = ldap.SUCCESS
  239. log.info(
  240. 'Search case 2-1. the search entry does not exist, the search should return no search results with %s' % rc.__name__)
  241. check_op_result(topology_st.standalone, 'search', BOGUSDN, None, exists, rc)
  242. exists = False
  243. rc = ldap.SUCCESS
  244. log.info(
  245. 'Search case 2-2. the search entry does not exist, the search should return no search results with %s' % rc.__name__)
  246. check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc)
  247. # Add
  248. exists = True
  249. rc = ldap.INSUFFICIENT_ACCESS
  250. log.info(
  251. 'Add case 1. the bind user has no rights AND the adding entry exists, it should fail with %s' % rc.__name__)
  252. check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc)
  253. exists = False
  254. rc = ldap.INSUFFICIENT_ACCESS
  255. log.info(
  256. 'Add case 2-1. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__)
  257. check_op_result(topology_st.standalone, 'add', BOGUSDN, None, exists, rc)
  258. exists = False
  259. rc = ldap.INSUFFICIENT_ACCESS
  260. log.info(
  261. 'Add case 2-2. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__)
  262. check_op_result(topology_st.standalone, 'add', BOGUSDN2, None, exists, rc)
  263. # Modify
  264. exists = True
  265. rc = ldap.INSUFFICIENT_ACCESS
  266. log.info(
  267. 'Modify case 1. the bind user has no rights AND the modifying entry exists, it should fail with %s' % rc.__name__)
  268. check_op_result(topology_st.standalone, 'modify', TESTDN, None, exists, rc)
  269. exists = False
  270. rc = ldap.INSUFFICIENT_ACCESS
  271. log.info(
  272. 'Modify case 2-1. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__)
  273. check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc)
  274. exists = False
  275. rc = ldap.INSUFFICIENT_ACCESS
  276. log.info(
  277. 'Modify case 2-2. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__)
  278. check_op_result(topology_st.standalone, 'modify', BOGUSDN2, None, exists, rc)
  279. # Modrdn
  280. exists = True
  281. rc = ldap.INSUFFICIENT_ACCESS
  282. log.info(
  283. 'Modrdn case 1. the bind user has no rights AND the renaming entry exists, it should fail with %s' % rc.__name__)
  284. check_op_result(topology_st.standalone, 'modrdn', TESTDN, None, exists, rc)
  285. exists = False
  286. rc = ldap.INSUFFICIENT_ACCESS
  287. log.info(
  288. 'Modrdn case 2-1. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__)
  289. check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc)
  290. exists = False
  291. rc = ldap.INSUFFICIENT_ACCESS
  292. log.info(
  293. 'Modrdn case 2-2. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__)
  294. check_op_result(topology_st.standalone, 'modrdn', BOGUSDN2, None, exists, rc)
  295. exists = True
  296. rc = ldap.INSUFFICIENT_ACCESS
  297. log.info(
  298. 'Modrdn case 3. the bind user has no rights AND the node moving an entry to exists, it should fail with %s' % rc.__name__)
  299. check_op_result(topology_st.standalone, 'modrdn', TESTDN, GROUPOU, exists, rc)
  300. exists = False
  301. rc = ldap.INSUFFICIENT_ACCESS
  302. log.info(
  303. 'Modrdn case 4-1. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__)
  304. check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
  305. exists = False
  306. rc = ldap.INSUFFICIENT_ACCESS
  307. log.info(
  308. 'Modrdn case 4-2. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__)
  309. check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
  310. # Delete
  311. exists = True
  312. rc = ldap.INSUFFICIENT_ACCESS
  313. log.info(
  314. 'Delete case 1. the bind user has no rights AND the deleting entry exists, it should fail with %s' % rc.__name__)
  315. check_op_result(topology_st.standalone, 'delete', TESTDN, None, exists, rc)
  316. exists = False
  317. rc = ldap.INSUFFICIENT_ACCESS
  318. log.info(
  319. 'Delete case 2-1. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__)
  320. check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc)
  321. exists = False
  322. rc = ldap.INSUFFICIENT_ACCESS
  323. log.info(
  324. 'Delete case 2-2. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__)
  325. check_op_result(topology_st.standalone, 'delete', BOGUSDN2, None, exists, rc)
  326. log.info('EXTRA: Check no regressions')
  327. log.info('Adding aci for %s to %s.' % (BINDDN, DEFAULT_SUFFIX))
  328. acival = '(targetattr="*")(version 3.0; acl "%s-all"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN)
  329. log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
  330. topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
  331. topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD, 'aci', acival)])
  332. time.sleep(1)
  333. log.info('Bind as {%s,%s}.' % (BINDDN, BINDPW))
  334. try:
  335. topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
  336. except ldap.LDAPError as e:
  337. log.info('Desc ' + e.message['desc'])
  338. assert False
  339. time.sleep(1)
  340. exists = False
  341. rc = ldap.NO_SUCH_OBJECT
  342. log.info('Search case. the search entry does not exist, the search should fail with %s' % rc.__name__)
  343. check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc)
  344. file_obj.close()
  345. exists = True
  346. rc = ldap.ALREADY_EXISTS
  347. log.info('Add case. the adding entry already exists, it should fail with %s' % rc.__name__)
  348. check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc)
  349. exists = False
  350. rc = ldap.NO_SUCH_OBJECT
  351. log.info('Modify case. the modifying entry does not exist, it should fail with %s' % rc.__name__)
  352. check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc)
  353. exists = False
  354. rc = ldap.NO_SUCH_OBJECT
  355. log.info('Modrdn case 1. the renaming entry does not exist, it should fail with %s' % rc.__name__)
  356. check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc)
  357. exists = False
  358. rc = ldap.NO_SUCH_OBJECT
  359. log.info('Modrdn case 2. the node moving an entry to does not, it should fail with %s' % rc.__name__)
  360. check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
  361. exists = False
  362. rc = ldap.NO_SUCH_OBJECT
  363. log.info('Delete case. the deleting entry does not exist, it should fail with %s' % rc.__name__)
  364. check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc)
  365. log.info('Inactivate %s' % BINDDN)
  366. nsinactivate = '%s/sbin/ns-inactivate.pl' % topology_st.standalone.prefix
  367. p = Popen([nsinactivate, '-Z', 'standalone', '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN])
  368. assert (p.wait() == 0)
  369. log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, BUID, ldap.UNWILLING_TO_PERFORM.__name__))
  370. try:
  371. topology_st.standalone.simple_bind_s(BINDDN, BUID)
  372. except ldap.LDAPError as e:
  373. log.info("Exception (expected): %s" % type(e).__name__)
  374. log.info('Desc ' + e.message['desc'])
  375. assert isinstance(e, ldap.UNWILLING_TO_PERFORM)
  376. log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, 'bogus', ldap.INVALID_CREDENTIALS.__name__))
  377. try:
  378. topology_st.standalone.simple_bind_s(BINDDN, 'bogus')
  379. except ldap.LDAPError as e:
  380. log.info("Exception (expected): %s" % type(e).__name__)
  381. log.info('Desc ' + e.message['desc'])
  382. assert isinstance(e, ldap.INVALID_CREDENTIALS)
  383. log.info('SUCCESS')
  384. if __name__ == '__main__':
  385. # Run isolated
  386. # -s for DEBUG mode
  387. CURRENT_FILE = os.path.realpath(__file__)
  388. pytest.main("-s %s" % CURRENT_FILE)