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 : }
|