Blame SOURCES/0014-Ticket-49950-PassSync-not-setting-pwdLastSet-attribu.patch

433de7
From 0d2456fd9e2678f6db075b224528727b741ff205 Mon Sep 17 00:00:00 2001
433de7
From: Mark Reynolds <mreynolds@redhat.com>
433de7
Date: Fri, 14 Sep 2018 11:24:35 -0400
433de7
Subject: [PATCH] Ticket 49950 -  PassSync not setting pwdLastSet attribute in
433de7
 Active Directory after Pw update from LDAP sync for normal user
433de7
433de7
Bug Description:
433de7
433de7
If a user's password was reset by an "Admin" or directory manager, the
433de7
password policy requires a user must change their password after it's
433de7
been "reset", and the user then resets their password in DS, this
433de7
information was not sent to AD.  Then if the user logged in AD after
433de7
resetting their password in DS they still get forced to change their
433de7
password again in AD.
433de7
433de7
Fix Description:
433de7
433de7
When sending a password update to AD, and AD is enforcing password must
433de7
be reset, check if the user's did reset thier password.  If so, set the
433de7
correct "pwdLastSet" value to prevent AD from forcing that user to
433de7
change their password again.
433de7
433de7
But this only works going from DS to AD.  The information needed to make
433de7
it work from AD -> DS is not available to passSync, and if it was available
433de7
it could not be correctly sent to DS anyway (not without a major redesign).
433de7
433de7
Side Note:
433de7
433de7
Also moved iand consolidated the function "fetch_attr" to util.c.  It
433de7
was reused and redefined in many plugins.  So I added the definition
433de7
to slapi-plugin.h and removed the duplicate definitions.
433de7
433de7
https://pagure.io/389-ds-base/issue/49950
433de7
433de7
Reviewed by: tbordaz(Thanks!)
433de7
433de7
(cherry picked from commit d9437be2e60fdbd6a5f1364f5887e1a3c89cda68)
433de7
(cherry picked from commit ac500d378aa22d5e818b110074ac9cd3e421e38d)
433de7
---
433de7
 ldap/servers/plugins/automember/automember.c  | 20 -----
433de7
 ldap/servers/plugins/linkedattrs/fixup_task.c | 20 -----
433de7
 ldap/servers/plugins/memberof/memberof.c      | 17 ----
433de7
 .../plugins/posix-winsync/posix-group-task.c  | 18 +---
433de7
 .../replication/repl5_replica_config.c        | 13 ---
433de7
 .../replication/windows_protocol_util.c       | 90 ++++++++++++++-----
433de7
 .../plugins/schema_reload/schema_reload.c     | 17 ----
433de7
 ldap/servers/plugins/syntaxes/validate_task.c | 20 -----
433de7
 ldap/servers/slapd/slapi-plugin.h             |  2 +
433de7
 ldap/servers/slapd/task.c                     | 17 ----
433de7
 ldap/servers/slapd/test-plugins/sampletask.c  | 16 ----
433de7
 ldap/servers/slapd/util.c                     | 17 ++++
433de7
 12 files changed, 89 insertions(+), 178 deletions(-)
433de7
433de7
diff --git a/ldap/servers/plugins/automember/automember.c b/ldap/servers/plugins/automember/automember.c
433de7
index c91aa4e8e..d982d49a3 100644
433de7
--- a/ldap/servers/plugins/automember/automember.c
433de7
+++ b/ldap/servers/plugins/automember/automember.c
433de7
@@ -74,7 +74,6 @@ static void automember_free_regex_rule(struct automemberRegexRule *rule);
433de7
 static int automember_parse_grouping_attr(char *value, char **grouping_attr, char **grouping_value);
433de7
 static int automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileDesc *ldif_fd);
433de7
 static int automember_add_member_value(Slapi_Entry *member_e, const char *group_dn, char *grouping_attr, char *grouping_value, PRFileDesc *ldif_fd);
433de7
-const char *fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val);
433de7
 
433de7
 /*
433de7
  * task functions
433de7
@@ -1927,25 +1926,6 @@ typedef struct _task_data
433de7
     int scope;
433de7
 } task_data;
433de7
 
433de7
-/*
433de7
- * extract a single value from the entry (as a string) -- if it's not in the
433de7
- * entry, the default will be returned (which can be NULL).
433de7
- * you do not need to free anything returned by this.
433de7
- */
433de7
-const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Value *val = NULL;
433de7
-    Slapi_Attr *attr;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0) {
433de7
-        return default_val;
433de7
-    }
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
-
433de7
 static void
433de7
 automember_task_destructor(Slapi_Task *task)
433de7
 {
433de7
diff --git a/ldap/servers/plugins/linkedattrs/fixup_task.c b/ldap/servers/plugins/linkedattrs/fixup_task.c
433de7
index 900ee1135..4929714b4 100644
433de7
--- a/ldap/servers/plugins/linkedattrs/fixup_task.c
433de7
+++ b/ldap/servers/plugins/linkedattrs/fixup_task.c
433de7
@@ -22,7 +22,6 @@ static void linked_attrs_fixup_task_thread(void *arg);
433de7
 static void linked_attrs_fixup_links(struct configEntry *config);
433de7
 static int linked_attrs_remove_backlinks_callback(Slapi_Entry *e, void *callback_data);
433de7
 static int linked_attrs_add_backlinks_callback(Slapi_Entry *e, void *callback_data);
433de7
-static const char *fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val);
433de7
 
433de7
 /*
433de7
  * Function Implementations
433de7
@@ -459,22 +458,3 @@ done:
433de7
 
433de7
     return rc;
433de7
 }
433de7
-
433de7
-/* extract a single value from the entry (as a string) -- if it's not in the
433de7
- * entry, the default will be returned (which can be NULL).
433de7
- * you do not need to free anything returned by this.
433de7
- */
433de7
-static const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Attr *attr;
433de7
-    Slapi_Value *val = NULL;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0) {
433de7
-        return default_val;
433de7
-    }
433de7
-
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
433de7
index 87313ff19..26236dc68 100644
433de7
--- a/ldap/servers/plugins/memberof/memberof.c
433de7
+++ b/ldap/servers/plugins/memberof/memberof.c
433de7
@@ -142,7 +142,6 @@ static int memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *con
433de7
 static int memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, Slapi_DN *group_sdn, Slapi_DN *op_this_sdn, Slapi_DN *replace_with_sdn, Slapi_DN *op_to_sdn, memberofstringll *stack);
433de7
 static int memberof_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
433de7
 static void memberof_task_destructor(Slapi_Task *task);
433de7
-static const char *fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val);
433de7
 static void memberof_fixup_task_thread(void *arg);
433de7
 static int memberof_fix_memberof(MemberOfConfig *config, Slapi_Task *task, task_data *td);
433de7
 static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data);
433de7
@@ -2871,22 +2870,6 @@ done:
433de7
                   "memberof_fixup_task_thread - refcount decremented.\n");
433de7
 }
433de7
 
433de7
-/* extract a single value from the entry (as a string) -- if it's not in the
433de7
- * entry, the default will be returned (which can be NULL).
433de7
- * you do not need to free anything returned by this.
433de7
- */
433de7
-const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Attr *attr;
433de7
-    Slapi_Value *val = NULL;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0)
433de7
-        return default_val;
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
-
433de7
 int
433de7
 memberof_task_add(Slapi_PBlock *pb,
433de7
                   Slapi_Entry *e,
433de7
diff --git a/ldap/servers/plugins/posix-winsync/posix-group-task.c b/ldap/servers/plugins/posix-winsync/posix-group-task.c
433de7
index b4c507595..d8b6addd4 100644
433de7
--- a/ldap/servers/plugins/posix-winsync/posix-group-task.c
433de7
+++ b/ldap/servers/plugins/posix-winsync/posix-group-task.c
433de7
@@ -42,22 +42,6 @@ posix_group_fixup_task_thread(void *arg);
433de7
 static int
433de7
 posix_group_fix_memberuid_callback(Slapi_Entry *e, void *callback_data);
433de7
 
433de7
-/* extract a single value from the entry (as a string) -- if it's not in the
433de7
- * entry, the default will be returned (which can be NULL).
433de7
- * you do not need to free anything returned by this.
433de7
- */
433de7
-static const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Attr *attr;
433de7
-    Slapi_Value *val = NULL;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0)
433de7
-        return default_val;
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
-
433de7
 /* e configEntry */
433de7
 int
433de7
 posix_group_task_add(Slapi_PBlock *pb __attribute__((unused)),
433de7
@@ -82,7 +66,7 @@ posix_group_task_add(Slapi_PBlock *pb __attribute__((unused)),
433de7
 
433de7
     /* get arg(s) */
433de7
     /* default: set replication basedn */
433de7
-    if ((dn = fetch_attr(e, "basedn", slapi_sdn_get_dn(posix_winsync_config_get_suffix()))) == NULL) {
433de7
+    if ((dn = fetch_attr(e, "basedn", (char *)slapi_sdn_get_dn(posix_winsync_config_get_suffix()))) == NULL) {
433de7
         *returncode = LDAP_OBJECT_CLASS_VIOLATION;
433de7
         rv = SLAPI_DSE_CALLBACK_ERROR;
433de7
         goto out;
433de7
diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c
433de7
index ea430d9a4..84e02639b 100644
433de7
--- a/ldap/servers/plugins/replication/repl5_replica_config.c
433de7
+++ b/ldap/servers/plugins/replication/repl5_replica_config.c
433de7
@@ -1353,19 +1353,6 @@ replica_execute_cleanruv_task(Object *r, ReplicaId rid, char *returntext __attri
433de7
     return LDAP_SUCCESS;
433de7
 }
433de7
 
433de7
-const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Attr *attr;
433de7
-    Slapi_Value *val = NULL;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0)
433de7
-        return default_val;
433de7
-
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
-
433de7
 static int
433de7
 replica_cleanall_ruv_task(Slapi_PBlock *pb __attribute__((unused)),
433de7
                           Slapi_Entry *e,
433de7
diff --git a/ldap/servers/plugins/replication/windows_protocol_util.c b/ldap/servers/plugins/replication/windows_protocol_util.c
433de7
index f350b6d34..f6898d018 100644
433de7
--- a/ldap/servers/plugins/replication/windows_protocol_util.c
433de7
+++ b/ldap/servers/plugins/replication/windows_protocol_util.c
433de7
@@ -720,39 +720,79 @@ send_password_modify(Slapi_DN *sdn,
433de7
     } else {
433de7
         Slapi_Attr *attr = NULL;
433de7
         int force_reset_pw = 0;
433de7
+        int pwd_already_reset = 0;
433de7
+        int ds_must_change = config_get_pw_must_change();
433de7
+
433de7
         /*
433de7
-             * If AD entry has password must change flag is set,
433de7
-             * we keep the flag (pwdLastSet == 0).
433de7
-             * msdn.microsoft.com: Windows Dev Centor - Desktop
433de7
-             * To force a user to change their password at next logon,
433de7
-             * set the pwdLastSet attribute to zero (0).
433de7
-             */
433de7
+         * If AD entry has password must change flag is set,
433de7
+         * we keep the flag (pwdLastSet == 0).
433de7
+         * msdn.microsoft.com: Windows Dev Centor - Desktop
433de7
+         * To force a user to change their password at next logon,
433de7
+         * set the pwdLastSet attribute to zero (0).
433de7
+         */
433de7
         if (remote_entry &&
433de7
             (0 == slapi_entry_attr_find(remote_entry, "pwdLastSet", &attr)) &&
433de7
-            attr) {
433de7
+            attr)
433de7
+        {
433de7
             Slapi_Value *v = NULL;
433de7
             int i = 0;
433de7
+
433de7
             for (i = slapi_attr_first_value(attr, &v);
433de7
                  v && (i != -1);
433de7
-                 i = slapi_attr_next_value(attr, i, &v)) {
433de7
+                 i = slapi_attr_next_value(attr, i, &v))
433de7
+            {
433de7
                 const char *s = slapi_value_get_string(v);
433de7
                 if (NULL == s) {
433de7
                     continue;
433de7
                 }
433de7
                 if (0 == strcmp(s, "0")) {
433de7
-                    slapi_log_err(SLAPI_LOG_REPL, windows_repl_plugin_name,
433de7
-                                  "%s: AD entry %s set \"user must change password at next logon\". ",
433de7
-                                  agmt_get_long_name(prp->agmt), slapi_entry_get_dn(remote_entry));
433de7
                     force_reset_pw = 1;
433de7
+                    if (ds_must_change) {
433de7
+                        /*
433de7
+                         * DS already enforces "password must be changed after reset".
433de7
+                         * Do an internal search and check the passwordExpirationtime
433de7
+                         * to see if is it actually needs to be reset.  If it doesn't,
433de7
+                         * then set pwdLastSet to -1
433de7
+                         */
433de7
+                        char *expiration_val;
433de7
+                        int rc = 0;
433de7
+                        Slapi_DN *local_sdn = NULL;
433de7
+
433de7
+                        rc = map_entry_dn_inbound(remote_entry, &local_sdn, prp->agmt);
433de7
+                        if ((0 == rc) && local_sdn) {
433de7
+                            Slapi_Entry *local_entry = NULL;
433de7
+                            /* Get the local entry if it exists */
433de7
+                            rc = windows_get_local_entry(local_sdn, &local_entry);
433de7
+                            if ((0 == rc) && local_entry) {
433de7
+                                expiration_val = (char *)fetch_attr(local_entry, "passwordExpirationtime", NULL);
433de7
+                                if (expiration_val && parse_genTime(expiration_val) != NO_TIME){
433de7
+                                    /* The user did reset their password */
433de7
+                                    slapi_log_err(SLAPI_LOG_REPL, windows_repl_plugin_name,
433de7
+                                        "send_password_modify - entry (%s) password was reset by user send that info to AD\n",
433de7
+                                        slapi_sdn_get_dn(local_sdn));
433de7
+                                    pwd_already_reset = 1;
433de7
+                                    force_reset_pw = 0;
433de7
+                                }
433de7
+                                slapi_entry_free(local_entry);
433de7
+                            }
433de7
+                        }
433de7
+                        slapi_sdn_free(&local_sdn);
433de7
+                    } else {
433de7
+                        slapi_log_err(SLAPI_LOG_REPL, windows_repl_plugin_name,
433de7
+                                      "%s: AD entry %s set \"user must change password at next logon\n",
433de7
+                                      agmt_get_long_name(prp->agmt), slapi_entry_get_dn(remote_entry));;
433de7
+                    }
433de7
                 }
433de7
             }
433de7
         }
433de7
-        /* We will attempt to bind to AD with the new password first. We do
433de7
-             * this to avoid playing a password change that originated from AD
433de7
-             * back to AD.  If we just played the password change back, then
433de7
-             * both sides would be in sync, but AD would contain the new password
433de7
-             * twice in it's password history, which undermines the password
433de7
-             * history policies in AD. */
433de7
+        /*
433de7
+         * We will attempt to bind to AD with the new password first. We do
433de7
+         * this to avoid playing a password change that originated from AD
433de7
+         * back to AD.  If we just played the password change back, then
433de7
+         * both sides would be in sync, but AD would contain the new password
433de7
+         * twice in it's password history, which undermines the password
433de7
+         * history policies in AD.
433de7
+         */
433de7
         if (windows_check_user_password(prp->conn, sdn, password)) {
433de7
             char *quoted_password = NULL;
433de7
             /* AD wants the password in quotes ! */
433de7
@@ -792,9 +832,18 @@ send_password_modify(Slapi_DN *sdn,
433de7
                     pw_mod.mod_bvalues = bvals;
433de7
 
433de7
                     pw_mods[0] = &pw_mod;
433de7
-                    if (force_reset_pw) {
433de7
-                        reset_bv.bv_len = 1;
433de7
-                        reset_bv.bv_val = "0";
433de7
+
433de7
+                    if (force_reset_pw || pwd_already_reset) {
433de7
+                        if (force_reset_pw) {
433de7
+                            reset_bv.bv_val = "0";
433de7
+                            reset_bv.bv_len = 1;
433de7
+                        } else if (pwd_already_reset) {
433de7
+                            /* Password was reset by the user, there is no
433de7
+                             * need to make the user change their password
433de7
+                             * again in AD so set pwdLastSet to -1 */
433de7
+                            reset_bv.bv_val = "-1";
433de7
+                            reset_bv.bv_len = 2;
433de7
+                        }
433de7
                         reset_bvals[0] = &reset_bv;
433de7
                         reset_bvals[1] = NULL;
433de7
                         reset_pw_mod.mod_type = "pwdLastSet";
433de7
@@ -807,7 +856,6 @@ send_password_modify(Slapi_DN *sdn,
433de7
                     }
433de7
 
433de7
                     pw_return = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(sdn), pw_mods, NULL, NULL);
433de7
-
433de7
                     slapi_ch_free((void **)&unicode_password);
433de7
                 }
433de7
                 PR_smprintf_free(quoted_password);
433de7
diff --git a/ldap/servers/plugins/schema_reload/schema_reload.c b/ldap/servers/plugins/schema_reload/schema_reload.c
433de7
index ee3b00c3c..c2399e5c3 100644
433de7
--- a/ldap/servers/plugins/schema_reload/schema_reload.c
433de7
+++ b/ldap/servers/plugins/schema_reload/schema_reload.c
433de7
@@ -187,23 +187,6 @@ schemareload_thread(void *arg)
433de7
                   "schemareload_thread <-- refcount decremented.\n");
433de7
 }
433de7
 
433de7
-/* extract a single value from the entry (as a string) -- if it's not in the
433de7
- * entry, the default will be returned (which can be NULL).
433de7
- * you do not need to free anything returned by this.
433de7
- */
433de7
-static const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Attr *attr;
433de7
-    Slapi_Value *val = NULL;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0)
433de7
-        return default_val;
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
-
433de7
 static void
433de7
 schemareload_destructor(Slapi_Task *task)
433de7
 {
433de7
diff --git a/ldap/servers/plugins/syntaxes/validate_task.c b/ldap/servers/plugins/syntaxes/validate_task.c
433de7
index 2c625ba71..afec9ef7a 100644
433de7
--- a/ldap/servers/plugins/syntaxes/validate_task.c
433de7
+++ b/ldap/servers/plugins/syntaxes/validate_task.c
433de7
@@ -43,7 +43,6 @@ static int syntax_validate_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entr
433de7
 static void syntax_validate_task_destructor(Slapi_Task *task);
433de7
 static void syntax_validate_task_thread(void *arg);
433de7
 static int syntax_validate_task_callback(Slapi_Entry *e, void *callback_data);
433de7
-static const char *fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val);
433de7
 static void syntax_validate_set_plugin_id(void *plugin_id);
433de7
 static void *syntax_validate_get_plugin_id(void);
433de7
 
433de7
@@ -258,25 +257,6 @@ bail:
433de7
     return rc;
433de7
 }
433de7
 
433de7
-/* extract a single value from the entry (as a string) -- if it's not in the
433de7
- * entry, the default will be returned (which can be NULL).
433de7
- * you do not need to free anything returned by this.
433de7
- */
433de7
-static const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Attr *attr;
433de7
-    Slapi_Value *val = NULL;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0) {
433de7
-        return default_val;
433de7
-    }
433de7
-
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
-
433de7
 /*
433de7
  * Plug-in identity management helper functions
433de7
  */
433de7
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
433de7
index 4b75654e7..bdad4e59e 100644
433de7
--- a/ldap/servers/slapd/slapi-plugin.h
433de7
+++ b/ldap/servers/slapd/slapi-plugin.h
433de7
@@ -8294,6 +8294,8 @@ int32_t slapi_atomic_decr_32(int32_t *ptr, int memorder);
433de7
  */
433de7
 uint64_t slapi_atomic_decr_64(uint64_t *ptr, int memorder);
433de7
 
433de7
+/* helper function */
433de7
+const char * fetch_attr(Slapi_Entry *e, const char *attrname, char *default_val);
433de7
 
433de7
 #ifdef __cplusplus
433de7
 }
433de7
diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c
433de7
index 3f9d5d995..698ee19b9 100644
433de7
--- a/ldap/servers/slapd/task.c
433de7
+++ b/ldap/servers/slapd/task.c
433de7
@@ -80,7 +80,6 @@ static void destroy_task(time_t when, void *arg);
433de7
 static int task_modify(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
433de7
 static int task_deny(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
433de7
 static void task_generic_destructor(Slapi_Task *task);
433de7
-static const char *fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val);
433de7
 static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn);
433de7
 static void modify_internal_entry(char *dn, LDAPMod **mods);
433de7
 static void fixup_tombstone_task_destructor(Slapi_Task *task);
433de7
@@ -684,22 +683,6 @@ destroy_task(time_t when, void *arg)
433de7
     slapi_ch_free((void **)&task);
433de7
 }
433de7
 
433de7
-/* extract a single value from the entry (as a string) -- if it's not in the
433de7
- * entry, the default will be returned (which can be NULL).
433de7
- * you do not need to free anything returned by this.
433de7
- */
433de7
-static const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Attr *attr;
433de7
-    Slapi_Value *val = NULL;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0)
433de7
-        return default_val;
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
-
433de7
 /* supply the pblock, destroy it when you're done */
433de7
 static Slapi_Entry *
433de7
 get_internal_entry(Slapi_PBlock *pb, char *dn)
433de7
diff --git a/ldap/servers/slapd/test-plugins/sampletask.c b/ldap/servers/slapd/test-plugins/sampletask.c
433de7
index d04f21b3d..22d43dd48 100644
433de7
--- a/ldap/servers/slapd/test-plugins/sampletask.c
433de7
+++ b/ldap/servers/slapd/test-plugins/sampletask.c
433de7
@@ -116,22 +116,6 @@ task_sampletask_thread(void *arg)
433de7
     slapi_task_finish(task, rv);
433de7
 }
433de7
 
433de7
-/* extract a single value from the entry (as a string) -- if it's not in the
433de7
- * entry, the default will be returned (which can be NULL).
433de7
- * you do not need to free anything returned by this.
433de7
- */
433de7
-static const char *
433de7
-fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val)
433de7
-{
433de7
-    Slapi_Attr *attr;
433de7
-    Slapi_Value *val = NULL;
433de7
-
433de7
-    if (slapi_entry_attr_find(e, attrname, &attr) != 0)
433de7
-        return default_val;
433de7
-    slapi_attr_first_value(attr, &val;;
433de7
-    return slapi_value_get_string(val);
433de7
-}
433de7
-
433de7
 static void
433de7
 task_sampletask_destructor(Slapi_Task *task)
433de7
 {
433de7
diff --git a/ldap/servers/slapd/util.c b/ldap/servers/slapd/util.c
433de7
index cb46efb3d..8563c5d27 100644
433de7
--- a/ldap/servers/slapd/util.c
433de7
+++ b/ldap/servers/slapd/util.c
433de7
@@ -1579,3 +1579,20 @@ slapi_create_errormsg(
433de7
         va_end(ap);
433de7
     }
433de7
 }
433de7
+
433de7
+/*
433de7
+ * Extract a single value from an entry (as a string) -- if it's not in the
433de7
+ * entry, the default will be returned (which can be NULL).  You do not need
433de7
+ * to free the returned string value.
433de7
+ */
433de7
+const char *
433de7
+fetch_attr(Slapi_Entry *e, const char *attrname, char *default_val)
433de7
+{
433de7
+    Slapi_Attr *attr;
433de7
+    Slapi_Value *val = NULL;
433de7
+
433de7
+    if (slapi_entry_attr_find(e, attrname, &attr) != 0)
433de7
+        return default_val;
433de7
+    slapi_attr_first_value(attr, &val;;
433de7
+    return slapi_value_get_string(val);
433de7
+}
433de7
-- 
433de7
2.17.2
433de7