Blame SOURCES/0095-Ticket-49742-Fine-grained-password-policy-can-impact.patch

92192e
From d1c87a502dc969198aa0e6a210e1303ae71bdeae Mon Sep 17 00:00:00 2001
92192e
From: Thierry Bordaz <tbordaz@redhat.com>
92192e
Date: Thu, 7 Jun 2018 18:35:34 +0200
92192e
Subject: [PATCH] Ticket 49742 - Fine grained password policy can impact search
92192e
 performance
92192e
92192e
Bug Description:
92192e
	new_passwdPolicy is called with an entry DN.
92192e
	In case of fine grain password policy we need to retrieve
92192e
	the possible password policy (pwdpolicysubentry) that applies to
92192e
	that entry.
92192e
	It triggers an internal search to retrieve the entry.
92192e
92192e
	In case of a search operation (add_shadow_ext_password_attrs), the
92192e
	entry is already in the pblock. So it is useless to do an additional
92192e
	internal search for it.
92192e
92192e
Fix Description:
92192e
	in case of fine grain password policy and a SRCH operation,
92192e
	if the entry DN matches the entry stored in the pblock (SLAPI_SEARCH_RESULT_ENTRY)
92192e
	then use that entry instead of doing an internal search
92192e
92192e
https://pagure.io/389-ds-base/issue/49742
92192e
92192e
Reviewed by: Mark Reynolds
92192e
92192e
Platforms tested: F26
92192e
92192e
Flag Day: no
92192e
92192e
Doc impact: no
92192e
---
92192e
 dirsrvtests/tests/tickets/ticket48228_test.py | 18 +++----
92192e
 dirsrvtests/tests/tickets/ticket548_test.py   | 18 ++++---
92192e
 ldap/servers/slapd/pw.c                       | 48 +++++++++++++++++--
92192e
 3 files changed, 66 insertions(+), 18 deletions(-)
92192e
92192e
diff --git a/dirsrvtests/tests/tickets/ticket48228_test.py b/dirsrvtests/tests/tickets/ticket48228_test.py
92192e
index 4f4494e0b..1ab741b94 100644
92192e
--- a/dirsrvtests/tests/tickets/ticket48228_test.py
92192e
+++ b/dirsrvtests/tests/tickets/ticket48228_test.py
92192e
@@ -7,6 +7,7 @@
92192e
 # --- END COPYRIGHT BLOCK ---
92192e
 #
92192e
 import logging
92192e
+import time
92192e
 
92192e
 import pytest
92192e
 from lib389.tasks import *
92192e
@@ -33,14 +34,14 @@ def set_global_pwpolicy(topology_st, inhistory):
92192e
     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
92192e
     # Enable password policy
92192e
     try:
92192e
-        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', 'on')])
92192e
+        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', b'on')])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
92192e
         assert False
92192e
 
92192e
     log.info("		Set global password history on\n")
92192e
     try:
92192e
-        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordHistory', 'on')])
92192e
+        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordHistory', b'on')])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set passwordHistory: error ' + e.message['desc'])
92192e
         assert False
92192e
@@ -48,7 +49,7 @@ def set_global_pwpolicy(topology_st, inhistory):
92192e
     log.info("		Set global passwords in history\n")
92192e
     try:
92192e
         count = "%d" % inhistory
92192e
-        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordInHistory', count)])
92192e
+        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordInHistory', count.encode())])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set passwordInHistory: error ' + e.message['desc'])
92192e
         assert False
92192e
@@ -113,9 +114,9 @@ def check_passwd_inhistory(topology_st, user, cpw, passwd):
92192e
     topology_st.standalone.simple_bind_s(user, cpw)
92192e
     time.sleep(1)
92192e
     try:
92192e
-        topology_st.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', passwd)])
92192e
+        topology_st.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', passwd.encode())])
92192e
     except ldap.LDAPError as e:
92192e
-        log.info('		The password ' + passwd + ' of user' + USER1_DN + ' in history: error ' + e.message['desc'])
92192e
+        log.info('		The password ' + passwd + ' of user' + USER1_DN + ' in history: error {0}'.format(e))
92192e
         inhistory = 1
92192e
     time.sleep(1)
92192e
     return inhistory
92192e
@@ -130,7 +131,7 @@ def update_passwd(topology_st, user, passwd, times):
92192e
         # Now update the value for this iter.
92192e
         cpw = 'password%d' % i
92192e
         try:
92192e
-            topology_st.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', cpw)])
92192e
+            topology_st.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', cpw.encode())])
92192e
         except ldap.LDAPError as e:
92192e
             log.fatal(
92192e
                 'test_ticket48228: Failed to update the password ' + cpw + ' of user ' + user + ': error ' + e.message[
92192e
@@ -146,7 +147,6 @@ def test_ticket48228_test_global_policy(topology_st):
92192e
     """
92192e
     Check global password policy
92192e
     """
92192e
-
92192e
     log.info('	Set inhistory = 6')
92192e
     set_global_pwpolicy(topology_st, 6)
92192e
 
92192e
@@ -201,7 +201,7 @@ def test_ticket48228_test_global_policy(topology_st):
92192e
     log.info("Global policy was successfully verified.")
92192e
 
92192e
 
92192e
-def test_ticket48228_test_subtree_policy(topology_st):
92192e
+def text_ticket48228_text_subtree_policy(topology_st):
92192e
     """
92192e
     Check subtree level password policy
92192e
     """
92192e
@@ -233,7 +233,7 @@ def test_ticket48228_test_subtree_policy(topology_st):
92192e
     log.info('	Set inhistory = 4')
92192e
     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
92192e
     try:
92192e
-        topology_st.standalone.modify_s(SUBTREE_PWP, [(ldap.MOD_REPLACE, 'passwordInHistory', '4')])
92192e
+        topology_st.standalone.modify_s(SUBTREE_PWP, [(ldap.MOD_REPLACE, 'passwordInHistory', b'4')])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
92192e
         assert False
92192e
diff --git a/dirsrvtests/tests/tickets/ticket548_test.py b/dirsrvtests/tests/tickets/ticket548_test.py
92192e
index d354cc802..0d71ab6ca 100644
92192e
--- a/dirsrvtests/tests/tickets/ticket548_test.py
92192e
+++ b/dirsrvtests/tests/tickets/ticket548_test.py
92192e
@@ -42,7 +42,7 @@ def set_global_pwpolicy(topology_st, min_=1, max_=10, warn=3):
92192e
     log.info("	+++++ Enable global password policy +++++\n")
92192e
     # Enable password policy
92192e
     try:
92192e
-        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', 'on')])
92192e
+        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', b'on')])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
92192e
         assert False
92192e
@@ -54,28 +54,28 @@ def set_global_pwpolicy(topology_st, min_=1, max_=10, warn=3):
92192e
 
92192e
     log.info("		Set global password Min Age -- %s day\n" % min_)
92192e
     try:
92192e
-        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMinAge', '%s' % min_secs)])
92192e
+        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMinAge', ('%s' % min_secs).encode())])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set passwordMinAge: error ' + e.message['desc'])
92192e
         assert False
92192e
 
92192e
     log.info("		Set global password Expiration -- on\n")
92192e
     try:
92192e
-        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordExp', 'on')])
92192e
+        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordExp', b'on')])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set passwordExp: error ' + e.message['desc'])
92192e
         assert False
92192e
 
92192e
     log.info("		Set global password Max Age -- %s days\n" % max_)
92192e
     try:
92192e
-        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMaxAge', '%s' % max_secs)])
92192e
+        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMaxAge', ('%s' % max_secs).encode())])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set passwordMaxAge: error ' + e.message['desc'])
92192e
         assert False
92192e
 
92192e
     log.info("		Set global password Warning -- %s days\n" % warn)
92192e
     try:
92192e
-        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordWarning', '%s' % warn_secs)])
92192e
+        topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordWarning', ('%s' % warn_secs).encode())])
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to set passwordWarning: error ' + e.message['desc'])
92192e
         assert False
92192e
@@ -93,6 +93,8 @@ def set_subtree_pwpolicy(topology_st, min_=2, max_=20, warn=6):
92192e
     try:
92192e
         topology_st.standalone.add_s(Entry((SUBTREE_CONTAINER, {'objectclass': 'top nsContainer'.split(),
92192e
                                                                 'cn': 'nsPwPolicyContainer'})))
92192e
+    except ldap.ALREADY_EXISTS:
92192e
+        pass
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to add subtree container: error ' + e.message['desc'])
92192e
         # assert False
92192e
@@ -128,6 +130,8 @@ def set_subtree_pwpolicy(topology_st, min_=2, max_=20, warn=6):
92192e
                                       'cosPriority': '1',
92192e
                                       'cn': SUBTREE_COS_TMPLDN,
92192e
                                       'pwdpolicysubentry': SUBTREE_PWP})))
92192e
+    except ldap.ALREADY_EXISTS:
92192e
+        pass
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to add COS template: error ' + e.message['desc'])
92192e
         # assert False
92192e
@@ -139,6 +143,8 @@ def set_subtree_pwpolicy(topology_st, min_=2, max_=20, warn=6):
92192e
                                      'cn': SUBTREE_PWPDN,
92192e
                                      'costemplatedn': SUBTREE_COS_TMPL,
92192e
                                      'cosAttribute': 'pwdpolicysubentry default operational-default'})))
92192e
+    except ldap.ALREADY_EXISTS:
92192e
+        pass
92192e
     except ldap.LDAPError as e:
92192e
         log.error('Failed to add COS def: error ' + e.message['desc'])
92192e
         # assert False
92192e
@@ -150,7 +156,7 @@ def update_passwd(topology_st, user, passwd, newpasswd):
92192e
     log.info("		Bind as {%s,%s}" % (user, passwd))
92192e
     topology_st.standalone.simple_bind_s(user, passwd)
92192e
     try:
92192e
-        topology_st.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', newpasswd)])
92192e
+        topology_st.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', newpasswd.encode())])
92192e
     except ldap.LDAPError as e:
92192e
         log.fatal('test_ticket548: Failed to update the password ' + cpw + ' of user ' + user + ': error ' + e.message[
92192e
             'desc'])
92192e
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
92192e
index 451be364d..10b8e7254 100644
92192e
--- a/ldap/servers/slapd/pw.c
92192e
+++ b/ldap/servers/slapd/pw.c
92192e
@@ -1625,6 +1625,10 @@ new_passwdPolicy(Slapi_PBlock *pb, const char *dn)
92192e
     int attr_free_flags = 0;
92192e
     int rc = 0;
92192e
     int optype = -1;
92192e
+    int free_e = 1; /* reset if e is taken from pb */
92192e
+    if (pb) {
92192e
+        slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &optype);
92192e
+    }
92192e
 
92192e
     /* If we already allocated a pw policy, return it */
92192e
     if (pb != NULL) {
92192e
@@ -1688,7 +1692,43 @@ new_passwdPolicy(Slapi_PBlock *pb, const char *dn)
92192e
             /*  If we're not doing an add, we look for the pwdpolicysubentry
92192e
             attribute in the target entry itself. */
92192e
         } else {
92192e
-            if ((e = get_entry(pb, dn)) != NULL) {
92192e
+            if (optype == SLAPI_OPERATION_SEARCH) {
92192e
+                Slapi_Entry *pb_e;
92192e
+
92192e
+                /* During a search the entry should be in the pblock
92192e
+                 * For safety check entry DN is identical to 'dn'
92192e
+                 */
92192e
+                slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &pb_e);
92192e
+                if (pb_e) {
92192e
+                    Slapi_DN * sdn;
92192e
+                    const char *ndn;
92192e
+                    char *pb_ndn;
92192e
+
92192e
+                    pb_ndn = slapi_entry_get_ndn(pb_e);
92192e
+
92192e
+                    sdn = slapi_sdn_new_dn_byval(dn);
92192e
+                    ndn = slapi_sdn_get_ndn(sdn);
92192e
+
92192e
+                    if (strcasecmp(pb_ndn, ndn) == 0) {
92192e
+                        /* We are using the candidate entry that is already loaded in the pblock
92192e
+                         * Do not trigger an additional internal search
92192e
+                         * Also we will not need to free the entry that will remain in the pblock
92192e
+                         */
92192e
+                        e = pb_e;
92192e
+                        free_e = 0;
92192e
+                    } else {
92192e
+                        e = get_entry(pb, dn);
92192e
+                    }
92192e
+                    slapi_sdn_free(&sdn;;
92192e
+                } else {
92192e
+                    e = get_entry(pb, dn);
92192e
+                }
92192e
+            } else {
92192e
+                /* For others operations but SEARCH */
92192e
+                e = get_entry(pb, dn);
92192e
+            }
92192e
+
92192e
+            if (e) {
92192e
                 Slapi_Attr *attr = NULL;
92192e
                 rc = slapi_entry_attr_find(e, "pwdpolicysubentry", &attr);
92192e
                 if (attr && (0 == rc)) {
92192e
@@ -1718,7 +1758,9 @@ new_passwdPolicy(Slapi_PBlock *pb, const char *dn)
92192e
                 }
92192e
             }
92192e
             slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags);
92192e
-            slapi_entry_free(e);
92192e
+            if (free_e) {
92192e
+                slapi_entry_free(e);
92192e
+            }
92192e
 
92192e
             if (pw_entry == NULL) {
92192e
                 slapi_log_err(SLAPI_LOG_ERR, "new_passwdPolicy",
92192e
@@ -1916,7 +1958,7 @@ new_passwdPolicy(Slapi_PBlock *pb, const char *dn)
92192e
                 slapi_pblock_set_pwdpolicy(pb, pwdpolicy);
92192e
             }
92192e
             return pwdpolicy;
92192e
-        } else if (e) {
92192e
+        } else if (free_e) {
92192e
             slapi_entry_free(e);
92192e
         }
92192e
     }
92192e
-- 
92192e
2.17.1
92192e