~ubuntu-branches/ubuntu/saucy/sssd/saucy

« back to all changes in this revision

Viewing changes to src/providers/ldap/ldap_id_cleanup.c

  • Committer: Stéphane Graber
  • Date: 2011-06-15 16:23:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: stgraber@ubuntu.com-20110615162314-rbhoppnpaxfqo5q7
Merge 1.5.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
37
struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
 
38
                                        struct tevent_context *ev,
 
39
                                        struct sdap_id_ctx *ctx);
 
40
static void ldap_id_cleanup_reschedule(struct tevent_req *req);
 
41
 
 
42
static void ldap_id_cleanup_timeout(struct tevent_context *ev,
 
43
                                      struct tevent_timer *te,
 
44
                                      struct timeval tv, void *pvt);
 
45
 
 
46
static void ldap_id_cleanup_timer(struct tevent_context *ev,
 
47
                                  struct tevent_timer *tt,
 
48
                                  struct timeval tv, void *pvt)
 
49
{
 
50
    struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx);
 
51
    struct tevent_timer *timeout;
 
52
    struct tevent_req *req;
 
53
    int delay;
 
54
    errno_t ret;
 
55
 
 
56
    if (be_is_offline(ctx->be)) {
 
57
        DEBUG(4, ("Backend is marked offline, retry later!\n"));
 
58
        /* schedule starting from now, not the last run */
 
59
        delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
 
60
        tv = tevent_timeval_current_ofs(delay, 0);
 
61
        ldap_id_cleanup_set_timer(ctx, tv);
 
62
        return;
 
63
    }
 
64
 
 
65
    req = ldap_id_cleanup_send(ctx, ev, ctx);
 
66
    if (!req) {
 
67
        DEBUG(1, ("Failed to schedule cleanup, retrying later!\n"));
 
68
        /* schedule starting from now, not the last run */
 
69
        delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
 
70
        tv = tevent_timeval_current_ofs(delay, 0);
 
71
        ret = ldap_id_cleanup_set_timer(ctx, tv);
 
72
        if (ret != EOK) {
 
73
            DEBUG(1, ("Error setting up cleanup timer\n"));
 
74
        }
 
75
        return;
 
76
    }
 
77
    tevent_req_set_callback(req, ldap_id_cleanup_reschedule, ctx);
 
78
 
 
79
    /* if cleanup takes so long, either we try to cleanup too
 
80
     * frequently, or something went seriously wrong */
 
81
    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
 
82
    tv = tevent_timeval_current_ofs(delay, 0);
 
83
    timeout = tevent_add_timer(ctx->be->ev, req, tv,
 
84
                               ldap_id_cleanup_timeout, req);
 
85
    if (timeout == NULL) {
 
86
        /* If we can't guarantee a timeout, we
 
87
         * need to cancel the request, to avoid
 
88
         * the possibility of starting another
 
89
         * concurrently
 
90
         */
 
91
        talloc_zfree(req);
 
92
 
 
93
        DEBUG(1, ("Failed to schedule cleanup, retrying later!\n"));
 
94
        /* schedule starting from now, not the last run */
 
95
        delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
 
96
        tv = tevent_timeval_current_ofs(delay, 0);
 
97
        ret = ldap_id_cleanup_set_timer(ctx, tv);
 
98
        if (ret != EOK) {
 
99
            DEBUG(1, ("Error setting up cleanup timer\n"));
 
100
        }
 
101
        return;
 
102
    }
 
103
    return;
 
104
}
 
105
 
 
106
static void ldap_id_cleanup_timeout(struct tevent_context *ev,
 
107
                                      struct tevent_timer *te,
 
108
                                      struct timeval tv, void *pvt)
 
109
{
 
110
    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
 
111
    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
 
112
                                                       struct sdap_id_ctx);
 
113
    int delay;
 
114
 
 
115
    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
 
116
    DEBUG(1, ("Cleanup timed out! Timeout too small? (%ds)!\n", delay));
 
117
 
 
118
    tv = tevent_timeval_current_ofs(delay, 0);
 
119
    ldap_id_cleanup_set_timer(ctx, tv);
 
120
 
 
121
    talloc_zfree(req);
 
122
}
 
123
 
 
124
static void ldap_id_cleanup_reschedule(struct tevent_req *req)
 
125
{
 
126
    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
 
127
                                                       struct sdap_id_ctx);
 
128
    enum tevent_req_state tstate;
 
129
    uint64_t err;
 
130
    struct timeval tv;
 
131
    int delay;
 
132
 
 
133
    if (tevent_req_is_error(req, &tstate, &err)) {
 
134
        /* On error schedule starting from now, not the last run */
 
135
        tv = tevent_timeval_current();
 
136
    } else {
 
137
        tv = ctx->last_purge;
 
138
    }
 
139
    talloc_zfree(req);
 
140
 
 
141
    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
 
142
    tv = tevent_timeval_add(&tv, delay, 0);
 
143
    ldap_id_cleanup_set_timer(ctx, tv);
 
144
}
 
145
 
 
146
 
 
147
 
 
148
int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
 
149
{
 
150
    struct tevent_timer *cleanup_task;
 
151
 
 
152
    DEBUG(6, ("Scheduling next cleanup at %ld.%ld\n",
 
153
              (long)tv.tv_sec, (long)tv.tv_usec));
 
154
 
 
155
    cleanup_task = tevent_add_timer(ctx->be->ev, ctx,
 
156
                                    tv, ldap_id_cleanup_timer, ctx);
 
157
    if (!cleanup_task) {
 
158
        DEBUG(0, ("FATAL: failed to setup cleanup task!\n"));
 
159
        return EFAULT;
 
160
    }
 
161
 
 
162
    return EOK;
 
163
}
 
164
 
 
165
 
 
166
 
 
167
struct global_cleanup_state {
 
168
    struct tevent_context *ev;
 
169
    struct sdap_id_ctx *ctx;
 
170
};
 
171
 
 
172
static int cleanup_users(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx);
 
173
static int cleanup_groups(TALLOC_CTX *memctx,
 
174
                          struct sysdb_ctx *sysdb,
 
175
                          struct sss_domain_info *domain);
 
176
 
 
177
struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
 
178
                                        struct tevent_context *ev,
 
179
                                        struct sdap_id_ctx *ctx)
 
180
{
 
181
    struct global_cleanup_state *state;
 
182
    struct tevent_req *req;
 
183
    int ret;
 
184
 
 
185
    req = tevent_req_create(memctx, &state, struct global_cleanup_state);
 
186
    if (!req) return NULL;
 
187
 
 
188
    state->ev = ev;
 
189
    state->ctx = ctx;
 
190
 
 
191
    ctx->last_purge = tevent_timeval_current();
 
192
 
 
193
    ret = cleanup_users(state, state->ctx);
 
194
    if (ret && ret != ENOENT) {
 
195
        goto fail;
 
196
    }
 
197
 
 
198
    ret = cleanup_groups(state,
 
199
                         state->ctx->be->sysdb,
 
200
                         state->ctx->be->domain);
 
201
    if (ret) {
 
202
        goto fail;
 
203
    }
 
204
 
 
205
    tevent_req_done(req);
 
206
    tevent_req_post(req, ev);
 
207
    return req;
 
208
 
 
209
fail:
 
210
    DEBUG(1, ("Failed to cleanup caches (%d [%s]), retrying later!\n",
 
211
              (int)ret, strerror(ret)));
 
212
    tevent_req_done(req);
 
213
    tevent_req_post(req, ev);
 
214
    return req;
 
215
}
 
216
 
 
217
 
 
218
/* ==User-Cleanup-Process================================================= */
 
219
 
 
220
static int cleanup_users_logged_in(hash_table_t *table,
 
221
                                   const struct ldb_message *msg);
 
222
 
 
223
static int cleanup_users(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx)
 
224
{
 
225
    TALLOC_CTX *tmpctx;
 
226
    struct sysdb_ctx *sysdb = ctx->be->sysdb;
 
227
    struct sss_domain_info *domain = ctx->be->domain;
 
228
    const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL };
 
229
    time_t now = time(NULL);
 
230
    char *subfilter = NULL;
 
231
    int account_cache_expiration;
 
232
    hash_table_t *uid_table;
 
233
    struct ldb_message **msgs;
 
234
    size_t count;
 
235
    const char *name;
 
236
    int ret;
 
237
    int i;
 
238
 
 
239
    tmpctx = talloc_new(memctx);
 
240
    if (!tmpctx) {
 
241
        return ENOMEM;
 
242
    }
 
243
 
 
244
    account_cache_expiration = dp_opt_get_int(ctx->opts->basic,
 
245
                                           SDAP_ACCOUNT_CACHE_EXPIRATION);
 
246
    DEBUG(9, ("Cache expiration is set to %d days\n",
 
247
              account_cache_expiration));
 
248
 
 
249
    if (account_cache_expiration > 0) {
 
250
        subfilter = talloc_asprintf(tmpctx,
 
251
                                    "(&(!(%s=0))(%s<=%ld)(|(!(%s=*))(%s<=%ld)))",
 
252
                                    SYSDB_CACHE_EXPIRE,
 
253
                                    SYSDB_CACHE_EXPIRE,
 
254
                                    (long) now,
 
255
                                    SYSDB_LAST_LOGIN,
 
256
                                    SYSDB_LAST_LOGIN,
 
257
                                    (long) (now - (account_cache_expiration * 86400)));
 
258
    } else {
 
259
        subfilter = talloc_asprintf(tmpctx,
 
260
                                    "(&(!(%s=0))(%s<=%ld)(!(%s=*)))",
 
261
                                    SYSDB_CACHE_EXPIRE,
 
262
                                    SYSDB_CACHE_EXPIRE,
 
263
                                    (long) now,
 
264
                                    SYSDB_LAST_LOGIN);
 
265
    }
 
266
    if (!subfilter) {
 
267
        DEBUG(2, ("Failed to build filter\n"));
 
268
        ret = ENOMEM;
 
269
        goto done;
 
270
    }
 
271
 
 
272
    ret = sysdb_search_users(tmpctx, sysdb,
 
273
                             domain, subfilter, attrs, &count, &msgs);
 
274
    if (ret) {
 
275
        if (ret == ENOENT) {
 
276
            ret = EOK;
 
277
        }
 
278
        goto done;
 
279
    }
 
280
 
 
281
    DEBUG(4, ("Found %d expired user entries!\n", count));
 
282
 
 
283
    if (count == 0) {
 
284
        ret = EOK;
 
285
        goto done;
 
286
    }
 
287
 
 
288
    ret = get_uid_table(tmpctx, &uid_table);
 
289
    /* get_uid_table returns ENOSYS on non-Linux platforms. We proceed with
 
290
     * the cleanup in that case
 
291
     */
 
292
    if (ret != EOK && ret != ENOSYS) {
 
293
        goto done;
 
294
    }
 
295
 
 
296
    for (i = 0; i < count; i++) {
 
297
        name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
 
298
        if (!name) {
 
299
            DEBUG(2, ("Entry %s has no Name Attribute ?!?\n",
 
300
                       ldb_dn_get_linearized(msgs[i]->dn)));
 
301
            ret = EFAULT;
 
302
            goto done;
 
303
        }
 
304
 
 
305
        if (uid_table) {
 
306
            ret = cleanup_users_logged_in(uid_table, msgs[i]);
 
307
            if (ret == EOK) {
 
308
                /* If the user is logged in, proceed to the next one */
 
309
                DEBUG(5, ("User %s is still logged in or a dummy entry, "
 
310
                          "keeping data\n", name));
 
311
                continue;
 
312
            } else if (ret != ENOENT) {
 
313
                goto done;
 
314
            }
 
315
        }
 
316
 
 
317
        /* If not logged in or cannot check the table, delete him */
 
318
        DEBUG(9, ("About to delete user %s\n", name));
 
319
        ret = sysdb_delete_user(tmpctx, sysdb, domain, name, 0);
 
320
        if (ret) {
 
321
            goto done;
 
322
        }
 
323
    }
 
324
 
 
325
done:
 
326
    talloc_zfree(tmpctx);
 
327
    return ret;
 
328
}
 
329
 
 
330
static int cleanup_users_logged_in(hash_table_t *table,
 
331
                                   const struct ldb_message *msg)
 
332
{
 
333
    uid_t      uid;
 
334
    hash_key_t key;
 
335
    hash_value_t value;
 
336
    int        ret;
 
337
 
 
338
    uid = ldb_msg_find_attr_as_uint64(msg,
 
339
                                      SYSDB_UIDNUM, 0);
 
340
    if (!uid) {
 
341
        DEBUG(2, ("Entry %s has no UID Attribute, fake user perhaps?\n",
 
342
                  ldb_dn_get_linearized(msg->dn)));
 
343
        return ENOENT;
 
344
    }
 
345
 
 
346
    key.type = HASH_KEY_ULONG;
 
347
    key.ul   = (unsigned long) uid;
 
348
 
 
349
    ret = hash_lookup(table, &key, &value);
 
350
    if (ret == HASH_SUCCESS) {
 
351
        return EOK;
 
352
    } else if (ret == HASH_ERROR_KEY_NOT_FOUND) {
 
353
        return ENOENT;
 
354
    }
 
355
 
 
356
    return EIO;
 
357
}
 
358
 
 
359
/* ==Group-Cleanup-Process================================================ */
 
360
 
 
361
static int cleanup_groups(TALLOC_CTX *memctx,
 
362
                          struct sysdb_ctx *sysdb,
 
363
                          struct sss_domain_info *domain)
 
364
{
 
365
    TALLOC_CTX *tmpctx;
 
366
    const char *attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL };
 
367
    time_t now = time(NULL);
 
368
    char *subfilter;
 
369
    const char *dn;
 
370
    gid_t gid;
 
371
    struct ldb_message **msgs;
 
372
    size_t count;
 
373
    struct ldb_message **u_msgs;
 
374
    size_t u_count;
 
375
    int ret;
 
376
    int i;
 
377
 
 
378
    tmpctx = talloc_new(memctx);
 
379
    if (!tmpctx) {
 
380
        return ENOMEM;
 
381
    }
 
382
 
 
383
    subfilter = talloc_asprintf(tmpctx, "(&(!(%s=0))(%s<=%ld))",
 
384
                                SYSDB_CACHE_EXPIRE,
 
385
                                SYSDB_CACHE_EXPIRE, (long)now);
 
386
    if (!subfilter) {
 
387
        DEBUG(2, ("Failed to build filter\n"));
 
388
        ret = ENOMEM;
 
389
        goto done;
 
390
    }
 
391
 
 
392
    ret = sysdb_search_groups(tmpctx, sysdb,
 
393
                              domain, subfilter, attrs, &count, &msgs);
 
394
    if (ret) {
 
395
        if (ret == ENOENT) {
 
396
            ret = EOK;
 
397
        }
 
398
        goto done;
 
399
    }
 
400
 
 
401
    DEBUG(4, ("Found %d expired group entries!\n", count));
 
402
 
 
403
    if (count == 0) {
 
404
        ret = EOK;
 
405
        goto done;
 
406
    }
 
407
 
 
408
    for (i = 0; i < count; i++) {
 
409
        dn = ldb_dn_get_linearized(msgs[i]->dn);
 
410
        if (!dn) {
 
411
            ret = EFAULT;
 
412
            goto done;
 
413
        }
 
414
 
 
415
        gid = (gid_t) ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0);
 
416
        if (!gid) {
 
417
            DEBUG(2, ("Entry has no GID\n"));
 
418
            ret = EIO;
 
419
            goto done;
 
420
        }
 
421
 
 
422
        /* Search for users that are members of this group, or
 
423
         * that have this group as their primary GID
 
424
         */
 
425
        subfilter = talloc_asprintf(tmpctx, "(|(%s=%s)(%s=%lu))",
 
426
                                    SYSDB_MEMBEROF, dn,
 
427
                                    SYSDB_GIDNUM, (long unsigned) gid);
 
428
        if (!subfilter) {
 
429
            DEBUG(2, ("Failed to build filter\n"));
 
430
            ret = ENOMEM;
 
431
            goto done;
 
432
        }
 
433
 
 
434
        ret = sysdb_search_users(tmpctx, sysdb,
 
435
                                 domain, subfilter, NULL, &u_count, &u_msgs);
 
436
        if (ret == ENOENT) {
 
437
            const char *name;
 
438
 
 
439
            name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
 
440
            if (!name) {
 
441
                DEBUG(2, ("Entry %s has no Name Attribute ?!?\n",
 
442
                          ldb_dn_get_linearized(msgs[i]->dn)));
 
443
                ret = EFAULT;
 
444
                goto done;
 
445
            }
 
446
 
 
447
            DEBUG(8, ("About to delete group %s\n", name));
 
448
            ret = sysdb_delete_group(tmpctx, sysdb, domain, name, 0);
 
449
            if (ret) {
 
450
                DEBUG(2, ("Group delete returned %d (%s)\n",
 
451
                          ret, strerror(ret)));
 
452
                goto done;
 
453
            }
 
454
        }
 
455
        if (ret != EOK) {
 
456
            goto done;
 
457
        }
 
458
        talloc_zfree(u_msgs);
 
459
    }
 
460
 
 
461
done:
 
462
    talloc_zfree(tmpctx);
 
463
    return ret;
 
464
}