LCOV - code coverage report
Current view: top level - providers/ldap - ldap_id_cleanup.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 80 214 37.4 %
Date: 2016-06-29 Functions: 3 7 42.9 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     LDAP Identity Cleanup Functions
       5             : 
       6             :     Authors:
       7             :         Simo Sorce <ssorce@redhat.com>
       8             : 
       9             :     Copyright (C) 2009 Red Hat
      10             : 
      11             :     This program is free software; you can redistribute it and/or modify
      12             :     it under the terms of the GNU General Public License as published by
      13             :     the Free Software Foundation; either version 3 of the License, or
      14             :     (at your option) any later version.
      15             : 
      16             :     This program is distributed in the hope that it will be useful,
      17             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :     GNU General Public License for more details.
      20             : 
      21             :     You should have received a copy of the GNU General Public License
      22             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      23             : */
      24             : 
      25             : #include <errno.h>
      26             : #include <time.h>
      27             : #include <sys/time.h>
      28             : 
      29             : #include "util/util.h"
      30             : #include "util/find_uid.h"
      31             : #include "db/sysdb.h"
      32             : #include "providers/ldap/ldap_common.h"
      33             : #include "providers/ldap/sdap_async.h"
      34             : 
      35             : /* ==Cleanup-Task========================================================= */
      36             : struct ldap_id_cleanup_ctx {
      37             :     struct sdap_id_ctx *ctx;
      38             :     struct sdap_domain *sdom;
      39             : };
      40             : 
      41           0 : static errno_t ldap_cleanup_task(TALLOC_CTX *mem_ctx,
      42             :                                  struct tevent_context *ev,
      43             :                                  struct be_ctx *be_ctx,
      44             :                                  struct be_ptask *be_ptask,
      45             :                                  void *pvt)
      46             : {
      47           0 :     struct ldap_id_cleanup_ctx *cleanup_ctx = NULL;
      48             : 
      49           0 :     cleanup_ctx = talloc_get_type(pvt, struct ldap_id_cleanup_ctx);
      50           0 :     return ldap_id_cleanup(cleanup_ctx->ctx->opts, cleanup_ctx->sdom);
      51             : }
      52             : 
      53           0 : errno_t ldap_setup_cleanup(struct sdap_id_ctx *id_ctx,
      54             :                            struct sdap_domain *sdom)
      55             : {
      56             :     errno_t ret;
      57             :     time_t first_delay;
      58             :     time_t period;
      59           0 :     struct ldap_id_cleanup_ctx *cleanup_ctx = NULL;
      60           0 :     char *name = NULL;
      61             : 
      62           0 :     period = dp_opt_get_int(id_ctx->opts->basic, SDAP_PURGE_CACHE_TIMEOUT);
      63           0 :     if (period == 0) {
      64             :         /* Cleanup has been explicitly disabled, so we won't
      65             :          * create any cleanup tasks. */
      66           0 :         ret = EOK;
      67           0 :         goto done;
      68             :     }
      69             : 
      70             :     /* Run the first one in a couple of seconds so that we have time to
      71             :      * finish initializations first. */
      72           0 :     first_delay = 10;
      73             : 
      74           0 :     cleanup_ctx = talloc_zero(sdom, struct ldap_id_cleanup_ctx);
      75           0 :     if (cleanup_ctx == NULL) {
      76           0 :         ret = ENOMEM;
      77           0 :         goto done;
      78             :     }
      79             : 
      80           0 :     cleanup_ctx->ctx = id_ctx;
      81           0 :     cleanup_ctx->sdom = sdom;
      82             : 
      83           0 :     name = talloc_asprintf(cleanup_ctx, "Cleanup of %s", sdom->dom->name);
      84           0 :     if (name == NULL) {
      85           0 :         return ENOMEM;
      86             :     }
      87             : 
      88           0 :     ret = be_ptask_create_sync(sdom, id_ctx->be, period, first_delay,
      89             :                                5 /* enabled delay */, 0 /* random offset */,
      90             :                                period /* timeout */, BE_PTASK_OFFLINE_SKIP, 0,
      91             :                                ldap_cleanup_task, cleanup_ctx, name,
      92             :                                &sdom->cleanup_task);
      93           0 :     if (ret != EOK) {
      94           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize cleanup periodic "
      95             :                                      "task for %s\n", sdom->dom->name);
      96           0 :         goto done;
      97             :     }
      98             : 
      99           0 :     talloc_steal(sdom->cleanup_task, cleanup_ctx);
     100           0 :     ret = EOK;
     101             : 
     102             : done:
     103           0 :     talloc_free(name);
     104           0 :     if (ret != EOK) {
     105           0 :         talloc_free(cleanup_ctx);
     106             :     }
     107             : 
     108           0 :     return ret;
     109             : }
     110             : 
     111             : static int cleanup_users(struct sdap_options *opts,
     112             :                          struct sss_domain_info *dom);
     113             : static int cleanup_groups(TALLOC_CTX *memctx,
     114             :                           struct sysdb_ctx *sysdb,
     115             :                           struct sss_domain_info *domain);
     116             : 
     117           2 : errno_t ldap_id_cleanup(struct sdap_options *opts,
     118             :                         struct sdap_domain *sdom)
     119             : {
     120             :     int ret, tret;
     121           2 :     bool in_transaction = false;
     122             :     TALLOC_CTX *tmp_ctx;
     123             : 
     124           2 :     tmp_ctx = talloc_new(NULL);
     125           2 :     if (tmp_ctx == NULL) {
     126           0 :         return ENOMEM;
     127             :     }
     128             : 
     129           2 :     ret = sysdb_transaction_start(sdom->dom->sysdb);
     130           2 :     if (ret != EOK) {
     131           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
     132           0 :         goto done;
     133             :     }
     134           2 :     in_transaction = true;
     135             : 
     136           2 :     ret = cleanup_users(opts, sdom->dom);
     137           2 :     if (ret && ret != ENOENT) {
     138           0 :         goto done;
     139             :     }
     140             : 
     141           2 :     ret = cleanup_groups(tmp_ctx, sdom->dom->sysdb, sdom->dom);
     142           2 :     if (ret) {
     143           0 :         goto done;
     144             :     }
     145             : 
     146           2 :     ret = sysdb_transaction_commit(sdom->dom->sysdb);
     147           2 :     if (ret != EOK) {
     148           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
     149           0 :         goto done;
     150             :     }
     151           2 :     in_transaction = false;
     152             : 
     153           2 :     sdom->last_purge = tevent_timeval_current();
     154           2 :     ret = EOK;
     155             : done:
     156           2 :     if (in_transaction) {
     157           0 :         tret = sysdb_transaction_cancel(sdom->dom->sysdb);
     158           0 :         if (tret != EOK) {
     159           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
     160             :         }
     161             :     }
     162           2 :     talloc_free(tmp_ctx);
     163           2 :     return ret;
     164             : }
     165             : 
     166             : 
     167             : /* ==User-Cleanup-Process================================================= */
     168             : 
     169             : static int cleanup_users_logged_in(hash_table_t *table,
     170             :                                    const struct ldb_message *msg);
     171             : 
     172             : static errno_t expire_memberof_target_groups(struct sss_domain_info *dom,
     173             :                                              struct ldb_message *user);
     174             : 
     175           2 : static int cleanup_users(struct sdap_options *opts,
     176             :                          struct sss_domain_info *dom)
     177             : {
     178             :     TALLOC_CTX *tmpctx;
     179           2 :     const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, SYSDB_MEMBEROF, NULL };
     180           2 :     time_t now = time(NULL);
     181           2 :     char *subfilter = NULL;
     182             :     int account_cache_expiration;
     183             :     hash_table_t *uid_table;
     184             :     struct ldb_message **msgs;
     185             :     size_t count;
     186             :     const char *name;
     187             :     int ret;
     188             :     int i;
     189             : 
     190           2 :     tmpctx = talloc_new(NULL);
     191           2 :     if (!tmpctx) {
     192           0 :         return ENOMEM;
     193             :     }
     194             : 
     195           2 :     account_cache_expiration = dp_opt_get_int(opts->basic, SDAP_ACCOUNT_CACHE_EXPIRATION);
     196           2 :     DEBUG(SSSDBG_TRACE_ALL, "Cache expiration is set to %d days\n",
     197             :               account_cache_expiration);
     198             : 
     199           2 :     if (account_cache_expiration > 0) {
     200           2 :         subfilter = talloc_asprintf(tmpctx,
     201             :                                     "(&(!(%s=0))(%s<=%ld)(|(!(%s=*))(%s<=%ld)))",
     202             :                                     SYSDB_CACHE_EXPIRE,
     203             :                                     SYSDB_CACHE_EXPIRE,
     204             :                                     (long) now,
     205             :                                     SYSDB_LAST_LOGIN,
     206             :                                     SYSDB_LAST_LOGIN,
     207           2 :                                     (long) (now - (account_cache_expiration * 86400)));
     208             :     } else {
     209           0 :         subfilter = talloc_asprintf(tmpctx,
     210             :                                     "(&(!(%s=0))(%s<=%ld)(!(%s=*)))",
     211             :                                     SYSDB_CACHE_EXPIRE,
     212             :                                     SYSDB_CACHE_EXPIRE,
     213             :                                     (long) now,
     214             :                                     SYSDB_LAST_LOGIN);
     215             :     }
     216           2 :     if (!subfilter) {
     217           0 :         DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
     218           0 :         ret = ENOMEM;
     219           0 :         goto done;
     220             :     }
     221             : 
     222           2 :     ret = sysdb_search_users(tmpctx, dom, subfilter, attrs, &count, &msgs);
     223           2 :     if (ret == ENOENT) {
     224           2 :         count = 0;
     225           0 :     } else if (ret != EOK) {
     226           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_search_users failed: %d\n", ret);
     227           0 :         goto done;
     228             :     }
     229           2 :     DEBUG(SSSDBG_FUNC_DATA, "Found %zu expired user entries!\n", count);
     230             : 
     231           2 :     if (count == 0) {
     232           2 :         ret = EOK;
     233           2 :         goto done;
     234             :     }
     235             : 
     236           0 :     ret = get_uid_table(tmpctx, &uid_table);
     237             :     /* get_uid_table returns ENOSYS on non-Linux platforms. We proceed with
     238             :      * the cleanup in that case
     239             :      */
     240           0 :     if (ret != EOK && ret != ENOSYS) {
     241           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "get_uid_table failed: %d\n", ret);
     242           0 :         goto done;
     243             :     }
     244             : 
     245           0 :     for (i = 0; i < count; i++) {
     246           0 :         name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
     247           0 :         if (!name) {
     248           0 :             DEBUG(SSSDBG_OP_FAILURE, "Entry %s has no Name Attribute ?!?\n",
     249             :                        ldb_dn_get_linearized(msgs[i]->dn));
     250           0 :             ret = EFAULT;
     251           0 :             goto done;
     252             :         }
     253           0 :         DEBUG(SSSDBG_TRACE_ALL, "Processing user %s\n", name);
     254             : 
     255           0 :         if (uid_table) {
     256           0 :             ret = cleanup_users_logged_in(uid_table, msgs[i]);
     257           0 :             if (ret == EOK) {
     258             :                 /* If the user is logged in, proceed to the next one */
     259           0 :                 DEBUG(SSSDBG_FUNC_DATA,
     260             :                       "User %s is still logged in or a dummy entry, "
     261             :                           "keeping data\n", name);
     262           0 :                 continue;
     263           0 :             } else if (ret != ENOENT) {
     264           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     265             :                       "Cannot check if user is logged in: %d\n", ret);
     266           0 :                 goto done;
     267             :             }
     268             :         }
     269             : 
     270             :         /* If not logged in or cannot check the table, delete him */
     271           0 :         DEBUG(SSSDBG_TRACE_ALL, "About to delete user %s\n", name);
     272           0 :         ret = sysdb_delete_user(dom, name, 0);
     273           0 :         if (ret) {
     274           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_user failed: %d\n", ret);
     275           0 :             goto done;
     276             :         }
     277             : 
     278             :         /* Mark all groups of which user was a member as expired in cache,
     279             :          * so that its ghost/member attributes are refreshed on next
     280             :          * request. */
     281           0 :         ret = expire_memberof_target_groups(dom, msgs[i]);
     282           0 :         if (ret != EOK && ret != ENOENT) {
     283           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     284             :                   "expire_memberof_target_groups failed: [%d]:%s\n",
     285             :                   ret, sss_strerror(ret));
     286           0 :             goto done;
     287             :         }
     288             :     }
     289             : 
     290             : done:
     291           2 :     talloc_zfree(tmpctx);
     292           2 :     return ret;
     293             : }
     294             : 
     295           0 : static errno_t expire_memberof_target_groups(struct sss_domain_info *dom,
     296             :                                              struct ldb_message *user)
     297             : {
     298           0 :     struct ldb_message_element *memberof_el = NULL;
     299             :     errno_t ret;
     300             :     TALLOC_CTX *tmp_ctx;
     301             : 
     302           0 :     tmp_ctx = talloc_new(NULL);
     303           0 :     if (tmp_ctx == NULL) {
     304           0 :         return ENOMEM;
     305             :     }
     306             : 
     307           0 :     memberof_el = ldb_msg_find_element(user, SYSDB_MEMBEROF);
     308           0 :     if (memberof_el == NULL) {
     309             :         /* User has no cached groups. Nothing to be marked as expired. */
     310           0 :         ret = EOK;
     311           0 :         goto done;
     312             :     }
     313             : 
     314           0 :     for (unsigned int i = 0; i < memberof_el->num_values; i++) {
     315           0 :         ret = sysdb_mark_entry_as_expired_ldb_val(dom,
     316           0 :                                                   &memberof_el->values[i]);
     317           0 :         if (ret != EOK && ret != ENOENT) {
     318           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     319             :                   "sysdb_mark_entry_as_expired_ldb_val failed: [%d]: %s\n",
     320             :                   ret, sss_strerror(ret));
     321           0 :             goto done;
     322             :         }
     323             :     }
     324             : 
     325           0 :     ret = EOK;
     326             : 
     327             : done:
     328           0 :     talloc_free(tmp_ctx);
     329           0 :     return ret;
     330             : }
     331             : 
     332           0 : static int cleanup_users_logged_in(hash_table_t *table,
     333             :                                    const struct ldb_message *msg)
     334             : {
     335             :     uid_t      uid;
     336             :     hash_key_t key;
     337             :     hash_value_t value;
     338             :     int        ret;
     339             : 
     340           0 :     uid = ldb_msg_find_attr_as_uint64(msg,
     341             :                                       SYSDB_UIDNUM, 0);
     342           0 :     if (!uid) {
     343           0 :         DEBUG(SSSDBG_OP_FAILURE, "Entry %s has no UID Attribute!\n",
     344             :                   ldb_dn_get_linearized(msg->dn));
     345           0 :         return ENOENT;
     346             :     }
     347             : 
     348           0 :     key.type = HASH_KEY_ULONG;
     349           0 :     key.ul   = (unsigned long) uid;
     350             : 
     351           0 :     ret = hash_lookup(table, &key, &value);
     352           0 :     if (ret == HASH_SUCCESS) {
     353           0 :         return EOK;
     354           0 :     } else if (ret == HASH_ERROR_KEY_NOT_FOUND) {
     355           0 :         return ENOENT;
     356             :     }
     357             : 
     358           0 :     DEBUG(SSSDBG_OP_FAILURE, "hash_lookup failed: %d\n", ret);
     359           0 :     return EIO;
     360             : }
     361             : 
     362             : /* ==Group-Cleanup-Process================================================ */
     363             : 
     364           2 : static int cleanup_groups(TALLOC_CTX *memctx,
     365             :                           struct sysdb_ctx *sysdb,
     366             :                           struct sss_domain_info *domain)
     367             : {
     368             :     TALLOC_CTX *tmpctx;
     369           2 :     const char *attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL };
     370           2 :     time_t now = time(NULL);
     371             :     char *subfilter;
     372             :     const char *dn;
     373             :     gid_t gid;
     374             :     struct ldb_message **msgs;
     375             :     size_t count;
     376             :     struct ldb_message **u_msgs;
     377             :     size_t u_count;
     378             :     int ret;
     379             :     int i;
     380             :     const char *posix;
     381             :     struct ldb_dn *base_dn;
     382             : 
     383           2 :     tmpctx = talloc_new(memctx);
     384           2 :     if (!tmpctx) {
     385           0 :         return ENOMEM;
     386             :     }
     387             : 
     388           2 :     subfilter = talloc_asprintf(tmpctx, "(&(!(%s=0))(%s<=%ld))",
     389             :                                 SYSDB_CACHE_EXPIRE,
     390             :                                 SYSDB_CACHE_EXPIRE, (long)now);
     391           2 :     if (!subfilter) {
     392           0 :         DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
     393           0 :         ret = ENOMEM;
     394           0 :         goto done;
     395             :     }
     396             : 
     397           2 :     ret = sysdb_search_groups(tmpctx, domain, subfilter, attrs, &count, &msgs);
     398           2 :     if (ret == ENOENT) {
     399           1 :         count = 0;
     400           1 :     } else if (ret != EOK) {
     401           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_search_groups failed: %d\n", ret);
     402           0 :         goto done;
     403             :     }
     404             : 
     405           2 :     DEBUG(SSSDBG_FUNC_DATA, "Found %zu expired group entries!\n", count);
     406             : 
     407           2 :     if (count == 0) {
     408           1 :         ret = EOK;
     409           1 :         goto done;
     410             :     }
     411             : 
     412          10 :     for (i = 0; i < count; i++) {
     413             :         char *sanitized_dn;
     414             : 
     415           4 :         dn = ldb_dn_get_linearized(msgs[i]->dn);
     416           4 :         if (!dn) {
     417           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Cannot linearize DN!\n");
     418           0 :             ret = EFAULT;
     419           0 :             goto done;
     420             :         }
     421             : 
     422             :         /* sanitize dn */
     423           4 :         ret = sss_filter_sanitize(tmpctx, dn, &sanitized_dn);
     424           4 :         if (ret != EOK) {
     425           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
     426             :                   "sss_filter_sanitize failed: %s:[%d]\n",
     427             :                   sss_strerror(ret), ret);
     428           0 :             goto done;
     429             :         }
     430             : 
     431           4 :         posix = ldb_msg_find_attr_as_string(msgs[i], SYSDB_POSIX, NULL);
     432           4 :         if (!posix || strcmp(posix, "TRUE") == 0) {
     433             :             /* Search for users that are members of this group, or
     434             :              * that have this group as their primary GID.
     435             :              * Include subdomain users as well.
     436             :              */
     437           4 :             gid = (gid_t) ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0);
     438           4 :             subfilter = talloc_asprintf(tmpctx, "(&(%s=%s)(|(%s=%s)(%s=%lu)))",
     439             :                                         SYSDB_OBJECTCLASS, SYSDB_USER_CLASS,
     440             :                                         SYSDB_MEMBEROF, sanitized_dn,
     441             :                                         SYSDB_GIDNUM, (long unsigned) gid);
     442             :         } else {
     443           0 :             subfilter = talloc_asprintf(tmpctx, "(%s=%s)", SYSDB_MEMBEROF,
     444             :                                         sanitized_dn);
     445             :         }
     446           4 :         talloc_zfree(sanitized_dn);
     447             : 
     448           4 :         if (!subfilter) {
     449           0 :             DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
     450           0 :             ret = ENOMEM;
     451           0 :             goto done;
     452             :         }
     453             : 
     454           4 :         base_dn = sysdb_base_dn(sysdb, tmpctx);
     455           4 :         if (base_dn == NULL) {
     456           0 :             DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
     457           0 :             ret = ENOMEM;
     458           0 :             goto done;
     459             :         }
     460             : 
     461           4 :         DEBUG(SSSDBG_TRACE_LIBS, "Searching with: %s\n", subfilter);
     462             : 
     463           4 :         ret = sysdb_search_entry(tmpctx, sysdb, base_dn,
     464             :                                  LDB_SCOPE_SUBTREE, subfilter, NULL,
     465             :                                  &u_count, &u_msgs);
     466           4 :         if (ret == ENOENT) {
     467             :             const char *name;
     468             : 
     469           2 :             name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
     470           2 :             if (!name) {
     471           0 :                 DEBUG(SSSDBG_OP_FAILURE, "Entry %s has no Name Attribute ?!?\n",
     472             :                           ldb_dn_get_linearized(msgs[i]->dn));
     473           0 :                 ret = EFAULT;
     474           0 :                 goto done;
     475             :             }
     476             : 
     477           2 :             DEBUG(SSSDBG_TRACE_INTERNAL, "About to delete group %s\n", name);
     478           2 :             ret = sysdb_delete_group(domain, name, 0);
     479           2 :             if (ret) {
     480           0 :                 DEBUG(SSSDBG_OP_FAILURE, "Group delete returned %d (%s)\n",
     481             :                           ret, strerror(ret));
     482           0 :                 goto done;
     483             :             }
     484           2 :         } else if (ret != EOK) {
     485           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     486             :                   "Failed to search sysdb using %s: [%d] %s\n",
     487             :                   subfilter, ret, sss_strerror(ret));
     488           0 :             goto done;
     489             :         }
     490           4 :         talloc_zfree(u_msgs);
     491             :     }
     492             : 
     493             : done:
     494           2 :     talloc_zfree(tmpctx);
     495           2 :     return ret;
     496             : }

Generated by: LCOV version 1.10