URL: https://github.com/SSSD/sssd/pull/943
Author: elkoniu
 Title: #943: files_ops: Fix cached password remove
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/943/head:pr943
git checkout pr943
From c08742bb662c777dbc5508b7c87ce75a126a9616 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Po=C5=82awski?= <ppola...@redhat.com>
Date: Thu, 14 Nov 2019 01:46:27 +0000
Subject: [PATCH] files_ops: Fix cached password remove

When SSSD daemon will detect refresh of password (group) file
it will delete all cached users (groups) data.
With this change cached data will be deleted only for non
existing users (groups).

Resolves:
https://pagure.io/SSSD/sssd/issue/3591
---
 src/db/sysdb.h                  |   5 +
 src/db/sysdb_ops.c              | 107 ++++++++++++++++++-
 src/providers/files/files_ops.c | 176 +++++++++++++++++++++++++++++++-
 3 files changed, 282 insertions(+), 6 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index e03c32d41d..45fff96914 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -898,6 +898,11 @@ int sysdb_delete_recursive(struct sysdb_ctx *sysdb,
                            struct ldb_dn *dn,
                            bool ignore_not_found);
 
+int sysdb_delete_recursive_with_whitelist(struct sysdb_ctx *sysdb,
+                                          struct ldb_dn *dn,
+                                          bool ignore_not_found,
+                                          const char **whitelist);
+
 int sysdb_delete_recursive_with_filter(struct sysdb_ctx *sysdb,
                                        struct ldb_dn *dn,
                                        bool ignore_not_found,
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index a108a7e60e..cb6cb6d2c3 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -233,13 +233,116 @@ int sysdb_delete_recursive_with_filter(struct sysdb_ctx *sysdb,
         goto done;
     }
 
-    DEBUG(SSSDBG_TRACE_ALL, "Found [%zu] items to delete.\n", msgs_count);
+    DEBUG(SSSDBG_TRACE_FUNC, "Found [%zu] items to delete.\n", msgs_count);
 
     qsort(msgs, msgs_count,
           sizeof(struct ldb_message *), compare_ldb_dn_comp_num);
 
     for (i = 0; i < msgs_count; i++) {
-        DEBUG(SSSDBG_TRACE_ALL, "Trying to delete [%s].\n",
+        DEBUG(SSSDBG_TRACE_FUNC, "Trying to delete [%s].\n",
+                  ldb_dn_get_linearized(msgs[i]->dn));
+
+        ret = sysdb_delete_entry(sysdb, msgs[i]->dn, false);
+        if (ret) {
+            goto done;
+        }
+    }
+
+done:
+    if (ret == EOK) {
+        ret = ldb_transaction_commit(sysdb->ldb);
+        ret = sysdb_error_to_errno(ret);
+    } else {
+        ldb_transaction_cancel(sysdb->ldb);
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+int sysdb_delete_recursive_with_whitelist(struct sysdb_ctx *sysdb,
+                                          struct ldb_dn *dn,
+                                          bool ignore_not_found,
+                                          const char **whitelist)
+{
+    const char *no_attrs[] = { NULL };
+    struct ldb_message **msgs;
+    size_t msgs_count;
+    int ret;
+    bool name_on_whitelist = false;
+    const char *linearized = NULL;
+    char *name_start = NULL;
+    const int NAME_MAX_LEN = 100;
+    char name[NAME_MAX_LEN];
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    ret = ldb_transaction_start(sysdb->ldb);
+    if (ret) {
+        ret = sysdb_error_to_errno(ret);
+        goto done;
+    }
+
+    /* Get all records using wildcard */
+    ret = sysdb_search_entry(tmp_ctx, sysdb, dn,
+                             LDB_SCOPE_SUBTREE, "(distinguishedName=*)",
+                             no_attrs, &msgs_count, &msgs);
+    if (ret) {
+        if (ignore_not_found && ret == ENOENT) {
+            ret = EOK;
+        }
+        if (ret) {
+            DEBUG(SSSDBG_TRACE_FUNC, "Search error: %d (%s)\n",
+                                     ret, strerror(ret));
+        }
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, "Found [%zu] items to delete.\n", msgs_count);
+
+    qsort(msgs, msgs_count,
+          sizeof(struct ldb_message *), compare_ldb_dn_comp_num);
+
+    /* Iterate over records found */
+    for (int i = 0; i < msgs_count; i++) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Evaluating record [%s]\n",
+              ldb_dn_get_linearized(msgs[i]->dn));
+
+        name_on_whitelist = false;
+        bzero(name, NAME_MAX_LEN);
+
+        /* Extract name value from linearized dn record */
+        linearized = ldb_dn_get_linearized(msgs[i]->dn);
+        name_start = strchr(linearized, '=') + sizeof(char);
+
+        for (size_t k = 0; name_start[k] != '@' && k < NAME_MAX_LEN - 1; k++) {
+            name[k] = name_start[k];
+        }
+
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Decoded name field: [%s]\n",
+              name);
+
+        for (size_t k = 0; whitelist[k]; k++) {
+            if (0 == strcmp(name, whitelist[k])) {
+                name_on_whitelist = true;
+                break;
+            }
+        }
+
+        /* Delete only entries not presented on whitelist */
+        if (name_on_whitelist) {
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Name [%s] found on whitelist, skipping\n",
+                  name);
+            continue;
+        }
+
+        DEBUG(SSSDBG_TRACE_FUNC, "Trying to delete [%s]\n",
                   ldb_dn_get_linearized(msgs[i]->dn));
 
         ret = sysdb_delete_entry(sysdb, msgs[i]->dn, false);
diff --git a/src/providers/files/files_ops.c b/src/providers/files/files_ops.c
index 59fc20692f..eafb10bfac 100644
--- a/src/providers/files/files_ops.c
+++ b/src/providers/files/files_ops.c
@@ -71,12 +71,13 @@ static errno_t enum_files_users(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    DEBUG(SSSDBG_TRACE_FUNC, "Enumerating passwd file: %s\n", passwd_file);
     while ((pwd_iter = fgetpwent(pwd_handle)) != NULL) {
         /* FIXME - we might want to support paging of sorts to avoid allocating
          * all users atop a memory context or only return users that differ from
          * the local storage as a diff to minimize memory spikes
          */
-        DEBUG(SSSDBG_TRACE_LIBS,
+        DEBUG(SSSDBG_TRACE_FUNC,
               "User found (%s, %s, %"SPRIuid", %"SPRIgid", %s, %s, %s)\n",
               pwd_iter->pw_name, pwd_iter->pw_passwd,
               pwd_iter->pw_uid, pwd_iter->pw_gid,
@@ -168,8 +169,9 @@ static errno_t enum_files_groups(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    DEBUG(SSSDBG_TRACE_FUNC, "Enumerating group file: %s\n", group_file);
     while ((grp_iter = fgetgrent(grp_handle)) != NULL) {
-        DEBUG(SSSDBG_TRACE_LIBS,
+        DEBUG(SSSDBG_TRACE_FUNC,
               "Group found (%s, %"SPRIgid")\n",
               grp_iter->gr_name, grp_iter->gr_gid);
 
@@ -272,6 +274,7 @@ static errno_t delete_all_users(struct sss_domain_info *dom)
     }
 
     ret = EOK;
+    DEBUG(SSSDBG_TRACE_FUNC, "All users deleted from database\n");
 
 done:
     talloc_free(tmp_ctx);
@@ -279,6 +282,88 @@ static errno_t delete_all_users(struct sss_domain_info *dom)
     return ret;
 }
 
+static errno_t delete_non_existing_users(struct files_id_ctx *id_ctx)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    TALLOC_CTX *tmp_ctx_users = NULL;
+    TALLOC_CTX *tmp_ctx_whitelist = NULL;
+    struct ldb_dn *base_dn;
+    struct passwd **users = NULL;
+    const char **whitelist = NULL;
+    int users_num = 0;
+    errno_t ret;
+
+    tmp_ctx_users = talloc_new(NULL);
+    if (tmp_ctx_users == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return ENOMEM;
+    }
+
+    /* Process password files to get existing users */
+    for (size_t i = 0; id_ctx->passwd_files[i] != NULL; i++) {
+        ret = enum_files_users(tmp_ctx_users,
+                               id_ctx->passwd_files[i],
+                               &users);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    tmp_ctx_whitelist = talloc_new(NULL);
+    if (tmp_ctx_whitelist == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return ENOMEM;
+    }
+
+    while (users[users_num]) {
+        users_num++;
+    }
+    DEBUG(SSSDBG_TRACE_FUNC, "Users whitelist size: %d\n", users_num);
+
+    whitelist = talloc_zero_array(tmp_ctx_whitelist, const char *, users_num + 1);
+    if (whitelist == NULL) {
+        goto done;
+    }
+
+    for (size_t i = 0; users[i]; i++) {
+        whitelist[i] = users[i]->pw_name;
+        DEBUG(SSSDBG_TRACE_FUNC, "Name added to whitelist: %s\n", whitelist[i]);
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return ENOMEM;
+    }
+
+    base_dn = sysdb_user_base_dn(tmp_ctx, id_ctx->domain);
+    if (base_dn == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_delete_recursive_with_whitelist(id_ctx->domain->sysdb,
+                                                base_dn,
+                                                true,
+                                                whitelist);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Unable to delete users subtree [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+    DEBUG(SSSDBG_TRACE_FUNC, "Non existing users deleted from database\n");
+
+done:
+    talloc_free(tmp_ctx);
+    talloc_free(tmp_ctx_whitelist);
+    talloc_free(tmp_ctx_users);
+
+    return ret;
+}
+
 static errno_t save_file_user(struct files_id_ctx *id_ctx,
                               struct passwd *pw)
 {
@@ -551,9 +636,92 @@ static errno_t delete_all_groups(struct sss_domain_info *dom)
     }
 
     ret = EOK;
+    DEBUG(SSSDBG_TRACE_FUNC, "All groups deleted from database\n");
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
+static errno_t delete_non_existing_groups(struct files_id_ctx *id_ctx)
+{
+    TALLOC_CTX *tmp_ctx;
+    TALLOC_CTX *tmp_ctx_groups = NULL;
+    TALLOC_CTX *tmp_ctx_whitelist = NULL;
+    struct ldb_dn *base_dn;
+    struct group **groups = NULL;
+    const char **whitelist = NULL;
+    int groups_num = 0;
+    errno_t ret;
+
+    tmp_ctx_groups = talloc_new(NULL);
+    if (tmp_ctx_groups == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return ENOMEM;
+    }
+
+    /* Process group files to get existing groups */
+    for (size_t i = 0; id_ctx->group_files[i] != NULL; i++) {
+        ret = enum_files_groups(tmp_ctx_groups,
+                                id_ctx->group_files[i],
+                                &groups);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    tmp_ctx_whitelist = talloc_new(NULL);
+    if (tmp_ctx_whitelist == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return ENOMEM;
+    }
+
+    while (groups[groups_num]) {
+        groups_num++;
+    }
+    DEBUG(SSSDBG_TRACE_FUNC, "Groups whitelist size: %d\n", groups_num);
+
+    whitelist = talloc_zero_array(tmp_ctx_whitelist, const char *, groups_num + 1);
+    if (whitelist == NULL) {
+        goto done;
+    }
+
+    for (size_t i = 0; groups[i]; i++) {
+        whitelist[i] = groups[i]->gr_name;
+        DEBUG(SSSDBG_TRACE_FUNC, "Name added to whitelist: %s\n", whitelist[i]);
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return ENOMEM;
+    }
+
+    base_dn = sysdb_group_base_dn(tmp_ctx, id_ctx->domain);
+    if (base_dn == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_delete_recursive_with_whitelist(id_ctx->domain->sysdb,
+                                                base_dn,
+                                                true,
+                                                whitelist);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Unable to delete groups subtree [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+    DEBUG(SSSDBG_TRACE_FUNC, "Non existing groups deleted from database\n");
 
 done:
     talloc_free(tmp_ctx);
+    talloc_free(tmp_ctx_whitelist);
+    talloc_free(tmp_ctx_groups);
 
     return ret;
 }
@@ -723,7 +891,7 @@ static errno_t sf_enum_files(struct files_id_ctx *id_ctx,
     in_transaction = true;
 
     if (flags & SF_UPDATE_PASSWD) {
-        ret = delete_all_users(id_ctx->domain);
+        ret = delete_non_existing_users(id_ctx);
         if (ret != EOK) {
             goto done;
         }
@@ -746,7 +914,7 @@ static errno_t sf_enum_files(struct files_id_ctx *id_ctx,
     }
 
     if (flags & SF_UPDATE_GROUP) {
-        ret = delete_all_groups(id_ctx->domain);
+        ret = delete_non_existing_groups(id_ctx);
         if (ret != EOK) {
             goto done;
         }
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: 
https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org

Reply via email to