zrhoffman / rpms / 389-ds-base

Forked from rpms/389-ds-base 3 years ago
Clone

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

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