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

« back to all changes in this revision

Viewing changes to src/providers/proxy/proxy_id.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
    proxy_id.c
 
5
 
 
6
    Authors:
 
7
        Stephen Gallagher <sgallagh@redhat.com>
 
8
 
 
9
    Copyright (C) 2010 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 "util/strtonum.h"
 
26
#include "providers/proxy/proxy.h"
 
27
 
 
28
/* =Getpwnam-wrapper======================================================*/
 
29
 
 
30
static int delete_user(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
 
31
                       struct sss_domain_info *domain, const char *name);
 
32
 
 
33
static int get_pw_name(TALLOC_CTX *mem_ctx,
 
34
                       struct proxy_id_ctx *ctx,
 
35
                       struct sysdb_ctx *sysdb,
 
36
                       struct sss_domain_info *dom,
 
37
                       const char *name)
 
38
{
 
39
    TALLOC_CTX *tmpctx;
 
40
    struct passwd *pwd;
 
41
    enum nss_status status;
 
42
    char *buffer;
 
43
    size_t buflen;
 
44
    int ret;
 
45
 
 
46
    DEBUG(7, ("Searching user by name (%s)\n", name));
 
47
 
 
48
    tmpctx = talloc_new(mem_ctx);
 
49
    if (!tmpctx) {
 
50
        return ENOMEM;
 
51
    }
 
52
 
 
53
    pwd = talloc_zero(tmpctx, struct passwd);
 
54
    if (!pwd) {
 
55
        ret = ENOMEM;
 
56
        status = NSS_STATUS_TRYAGAIN;
 
57
        goto done;
 
58
    }
 
59
 
 
60
    buflen = DEFAULT_BUFSIZE;
 
61
    buffer = talloc_size(tmpctx, buflen);
 
62
    if (!buffer) {
 
63
        ret = ENOMEM;
 
64
        status = NSS_STATUS_TRYAGAIN;
 
65
        goto done;
 
66
    }
 
67
 
 
68
    /* FIXME: should we move this call outside the transaction to keep the
 
69
     * transaction as short as possible ? */
 
70
    status = ctx->ops.getpwnam_r(name, pwd, buffer, buflen, &ret);
 
71
 
 
72
    switch (status) {
 
73
    case NSS_STATUS_NOTFOUND:
 
74
 
 
75
        DEBUG(7, ("User %s not found.\n", name));
 
76
        ret = delete_user(tmpctx, sysdb, dom, name);
 
77
        if (ret) {
 
78
            goto done;
 
79
        }
 
80
        break;
 
81
 
 
82
    case NSS_STATUS_SUCCESS:
 
83
 
 
84
        DEBUG(7, ("User %s found: (%s, %d, %d)\n",
 
85
                  name, pwd->pw_name, pwd->pw_uid, pwd->pw_gid));
 
86
 
 
87
        /* uid=0 or gid=0 are invalid values */
 
88
        /* also check that the id is in the valid range for this domain */
 
89
        if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
 
90
            OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
 
91
 
 
92
            DEBUG(2, ("User [%s] filtered out! (id out of range)\n", name));
 
93
            ret = delete_user(tmpctx, sysdb, dom, name);
 
94
            if (ret) {
 
95
                goto done;
 
96
            }
 
97
            break;
 
98
        }
 
99
 
 
100
        ret = sysdb_store_user(tmpctx, sysdb, dom,
 
101
                               pwd->pw_name,
 
102
                               pwd->pw_passwd,
 
103
                               pwd->pw_uid,
 
104
                               pwd->pw_gid,
 
105
                               pwd->pw_gecos,
 
106
                               pwd->pw_dir,
 
107
                               pwd->pw_shell,
 
108
                               NULL, NULL,
 
109
                               ctx->entry_cache_timeout);
 
110
        if (ret) {
 
111
            goto done;
 
112
        }
 
113
        break;
 
114
 
 
115
    case NSS_STATUS_UNAVAIL:
 
116
        /* "remote" backend unavailable. Enter offline mode */
 
117
        ret = ENXIO;
 
118
        goto done;
 
119
 
 
120
    default:
 
121
        ret = EIO;
 
122
        goto done;
 
123
    }
 
124
 
 
125
done:
 
126
    talloc_zfree(tmpctx);
 
127
    if (ret) {
 
128
        DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n",
 
129
                  name, status));
 
130
    }
 
131
    return ret;
 
132
}
 
133
 
 
134
static int delete_user(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
 
135
                       struct sss_domain_info *domain, const char *name)
 
136
{
 
137
    struct ldb_dn *dn;
 
138
 
 
139
    DEBUG(7, ("User %s does not exist (or is invalid) on remote server,"
 
140
              " deleting!\n", name));
 
141
 
 
142
    dn = sysdb_user_dn(sysdb, mem_ctx, domain->name, name);
 
143
    if (!dn) {
 
144
        return ENOMEM;
 
145
    }
 
146
 
 
147
    return sysdb_delete_entry(sysdb, dn, true);
 
148
}
 
149
 
 
150
/* =Getpwuid-wrapper======================================================*/
 
151
 
 
152
static int get_pw_uid(TALLOC_CTX *mem_ctx,
 
153
                      struct proxy_id_ctx *ctx,
 
154
                      struct sysdb_ctx *sysdb,
 
155
                      struct sss_domain_info *dom,
 
156
                      uid_t uid)
 
157
{
 
158
    TALLOC_CTX *tmpctx;
 
159
    struct passwd *pwd;
 
160
    enum nss_status status;
 
161
    char *buffer;
 
162
    size_t buflen;
 
163
    bool del_user = false;
 
164
    int ret;
 
165
 
 
166
    DEBUG(7, ("Searching user by uid (%d)\n", uid));
 
167
 
 
168
    tmpctx = talloc_new(mem_ctx);
 
169
    if (!tmpctx) {
 
170
        return ENOMEM;
 
171
    }
 
172
 
 
173
    pwd = talloc_zero(tmpctx, struct passwd);
 
174
    if (!pwd) {
 
175
        ret = ENOMEM;
 
176
        DEBUG(1, ("proxy -> getpwuid_r failed for '%d': [%d] %s\n",
 
177
                  uid, ret, strerror(ret)));
 
178
        return ret;
 
179
    }
 
180
 
 
181
    buflen = DEFAULT_BUFSIZE;
 
182
    buffer = talloc_size(tmpctx, buflen);
 
183
    if (!buffer) {
 
184
        ret = ENOMEM;
 
185
        DEBUG(1, ("proxy -> getpwuid_r failed for '%d': [%d] %s\n",
 
186
                  uid, ret, strerror(ret)));
 
187
        return ret;
 
188
    }
 
189
 
 
190
    status = ctx->ops.getpwuid_r(uid, pwd, buffer, buflen, &ret);
 
191
 
 
192
    switch (status) {
 
193
    case NSS_STATUS_NOTFOUND:
 
194
 
 
195
        DEBUG(7, ("User %d not found.\n", uid));
 
196
        del_user = true;
 
197
        break;
 
198
 
 
199
    case NSS_STATUS_SUCCESS:
 
200
 
 
201
        DEBUG(7, ("User %d found (%s, %d, %d)\n",
 
202
                  uid, pwd->pw_name, pwd->pw_uid, pwd->pw_gid));
 
203
 
 
204
        /* uid=0 or gid=0 are invalid values */
 
205
        /* also check that the id is in the valid range for this domain */
 
206
        if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
 
207
            OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
 
208
 
 
209
            DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
 
210
                      pwd->pw_name));
 
211
            del_user = true;
 
212
            break;
 
213
        }
 
214
 
 
215
        ret = sysdb_store_user(tmpctx, sysdb, dom,
 
216
                               pwd->pw_name,
 
217
                               pwd->pw_passwd,
 
218
                               pwd->pw_uid,
 
219
                               pwd->pw_gid,
 
220
                               pwd->pw_gecos,
 
221
                               pwd->pw_dir,
 
222
                               pwd->pw_shell,
 
223
                               NULL, NULL,
 
224
                               ctx->entry_cache_timeout);
 
225
        if (ret) {
 
226
            goto done;
 
227
        }
 
228
        break;
 
229
 
 
230
    case NSS_STATUS_UNAVAIL:
 
231
        /* "remote" backend unavailable. Enter offline mode */
 
232
        ret = ENXIO;
 
233
        goto done;
 
234
 
 
235
    default:
 
236
        ret = EIO;
 
237
        goto done;
 
238
    }
 
239
 
 
240
    if (del_user) {
 
241
        DEBUG(7, ("User %d does not exist (or is invalid) on remote server,"
 
242
                  " deleting!\n", uid));
 
243
 
 
244
        ret = sysdb_delete_user(tmpctx, sysdb, dom, NULL, uid);
 
245
        if (ret) {
 
246
            goto done;
 
247
        }
 
248
    }
 
249
 
 
250
done:
 
251
    talloc_zfree(tmpctx);
 
252
    if (ret) {
 
253
        DEBUG(2, ("proxy -> getpwuid_r failed for '%d' <%d>\n", uid, status));
 
254
    }
 
255
    return ret;
 
256
}
 
257
 
 
258
/* =Getpwent-wrapper======================================================*/
 
259
 
 
260
static int enum_users(TALLOC_CTX *mem_ctx,
 
261
                      struct proxy_id_ctx *ctx,
 
262
                      struct sysdb_ctx *sysdb,
 
263
                      struct sss_domain_info *dom)
 
264
{
 
265
    TALLOC_CTX *tmpctx;
 
266
    bool in_transaction = false;
 
267
    struct passwd *pwd;
 
268
    enum nss_status status;
 
269
    size_t buflen;
 
270
    char *buffer;
 
271
    char *newbuf;
 
272
    int ret;
 
273
 
 
274
    DEBUG(7, ("Enumerating users\n"));
 
275
 
 
276
    tmpctx = talloc_new(mem_ctx);
 
277
    if (!tmpctx) {
 
278
        return ENOMEM;
 
279
    }
 
280
 
 
281
    pwd = talloc_zero(tmpctx, struct passwd);
 
282
    if (!pwd) {
 
283
        ret = ENOMEM;
 
284
        goto done;
 
285
    }
 
286
 
 
287
    buflen = DEFAULT_BUFSIZE;
 
288
    buffer = talloc_size(tmpctx, buflen);
 
289
    if (!buffer) {
 
290
        ret = ENOMEM;
 
291
        goto done;
 
292
    }
 
293
 
 
294
    ret = sysdb_transaction_start(sysdb);
 
295
    if (ret) {
 
296
        goto done;
 
297
    }
 
298
    in_transaction = true;
 
299
 
 
300
    status = ctx->ops.setpwent();
 
301
    if (status != NSS_STATUS_SUCCESS) {
 
302
        ret = EIO;
 
303
        goto done;
 
304
    }
 
305
 
 
306
again:
 
307
    /* always zero out the pwd structure */
 
308
    memset(pwd, 0, sizeof(struct passwd));
 
309
 
 
310
    /* get entry */
 
311
    status = ctx->ops.getpwent_r(pwd, buffer, buflen, &ret);
 
312
 
 
313
    switch (status) {
 
314
    case NSS_STATUS_TRYAGAIN:
 
315
        /* buffer too small ? */
 
316
        if (buflen < MAX_BUF_SIZE) {
 
317
            buflen *= 2;
 
318
        }
 
319
        if (buflen > MAX_BUF_SIZE) {
 
320
            buflen = MAX_BUF_SIZE;
 
321
        }
 
322
        newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
 
323
        if (!newbuf) {
 
324
            ret = ENOMEM;
 
325
            goto done;
 
326
        }
 
327
        buffer = newbuf;
 
328
        goto again;
 
329
 
 
330
    case NSS_STATUS_NOTFOUND:
 
331
 
 
332
        /* we are done here */
 
333
        DEBUG(7, ("Enumeration completed.\n"));
 
334
 
 
335
        ret = sysdb_transaction_commit(sysdb);
 
336
        in_transaction = false;
 
337
        break;
 
338
 
 
339
    case NSS_STATUS_SUCCESS:
 
340
 
 
341
        DEBUG(7, ("User found (%s, %d, %d)\n",
 
342
                  pwd->pw_name, pwd->pw_uid, pwd->pw_gid));
 
343
 
 
344
        /* uid=0 or gid=0 are invalid values */
 
345
        /* also check that the id is in the valid range for this domain */
 
346
        if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
 
347
            OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
 
348
 
 
349
            DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
 
350
                      pwd->pw_name));
 
351
 
 
352
            goto again; /* skip */
 
353
        }
 
354
 
 
355
        ret = sysdb_store_user(tmpctx, sysdb, dom,
 
356
                               pwd->pw_name,
 
357
                               pwd->pw_passwd,
 
358
                               pwd->pw_uid,
 
359
                               pwd->pw_gid,
 
360
                               pwd->pw_gecos,
 
361
                               pwd->pw_dir,
 
362
                               pwd->pw_shell,
 
363
                               NULL, NULL,
 
364
                               ctx->entry_cache_timeout);
 
365
        if (ret) {
 
366
            /* Do not fail completely on errors.
 
367
             * Just report the failure to save and go on */
 
368
            DEBUG(2, ("Failed to store user %s. Ignoring.\n",
 
369
                      pwd->pw_name));
 
370
        }
 
371
        goto again; /* next */
 
372
 
 
373
    case NSS_STATUS_UNAVAIL:
 
374
        /* "remote" backend unavailable. Enter offline mode */
 
375
        ret = ENXIO;
 
376
        break;
 
377
 
 
378
    default:
 
379
        ret = EIO;
 
380
        DEBUG(2, ("proxy -> getpwent_r failed (%d)[%s]\n",
 
381
                  ret, strerror(ret)));
 
382
        break;
 
383
    }
 
384
 
 
385
done:
 
386
    talloc_zfree(tmpctx);
 
387
    if (in_transaction) {
 
388
        sysdb_transaction_cancel(sysdb);
 
389
    }
 
390
    ctx->ops.endpwent();
 
391
    return ret;
 
392
}
 
393
 
 
394
/* =Getgrnam-wrapper======================================================*/
 
395
 
 
396
#define DEBUG_GR_MEM(level, grp) \
 
397
    do { \
 
398
        if (debug_level >= level) { \
 
399
            if (!grp->gr_mem || !grp->gr_mem[0]) { \
 
400
                DEBUG(level, ("Group %s has no members!\n", \
 
401
                              grp->gr_name)); \
 
402
            } else { \
 
403
                int i = 0; \
 
404
                while (grp->gr_mem[i]) { \
 
405
                    /* count */ \
 
406
                    i++; \
 
407
                } \
 
408
                DEBUG(level, ("Group %s has %d members!\n", \
 
409
                              grp->gr_name, i)); \
 
410
            } \
 
411
        } \
 
412
    } while(0)
 
413
 
 
414
static int get_gr_name(TALLOC_CTX *mem_ctx,
 
415
                       struct proxy_id_ctx *ctx,
 
416
                       struct sysdb_ctx *sysdb,
 
417
                       struct sss_domain_info *dom,
 
418
                       const char *name)
 
419
{
 
420
    TALLOC_CTX *tmpctx;
 
421
    struct group *grp;
 
422
    enum nss_status status;
 
423
    char *buffer;
 
424
    char *newbuf;
 
425
    size_t buflen;
 
426
    bool delete_group = false;
 
427
    struct sysdb_attrs *members;
 
428
    int ret;
 
429
 
 
430
    DEBUG(7, ("Searching group by name (%s)\n", name));
 
431
 
 
432
    tmpctx = talloc_new(mem_ctx);
 
433
    if (!tmpctx) {
 
434
        return ENOMEM;
 
435
    }
 
436
 
 
437
    grp = talloc(tmpctx, struct group);
 
438
    if (!grp) {
 
439
        ret = ENOMEM;
 
440
        DEBUG(1, ("proxy -> getgrnam_r failed for '%s': [%d] %s\n",
 
441
                  name, ret, strerror(ret)));
 
442
        return ret;
 
443
    }
 
444
 
 
445
    buflen = DEFAULT_BUFSIZE;
 
446
    buffer = talloc_size(tmpctx, buflen);
 
447
    if (!buffer) {
 
448
        ret = ENOMEM;
 
449
        DEBUG(1, ("proxy -> getgrnam_r failed for '%s': [%d] %s\n",
 
450
                  name, ret, strerror(ret)));
 
451
        return ret;
 
452
    }
 
453
 
 
454
    /* FIXME: should we move this call outside the transaction to keep the
 
455
     * transaction as short as possible ? */
 
456
again:
 
457
    /* always zero out the grp structure */
 
458
    memset(grp, 0, sizeof(struct group));
 
459
 
 
460
    status = ctx->ops.getgrnam_r(name, grp, buffer, buflen, &ret);
 
461
 
 
462
    switch (status) {
 
463
    case NSS_STATUS_TRYAGAIN:
 
464
        /* buffer too small ? */
 
465
        if (buflen < MAX_BUF_SIZE) {
 
466
            buflen *= 2;
 
467
        }
 
468
        if (buflen > MAX_BUF_SIZE) {
 
469
            buflen = MAX_BUF_SIZE;
 
470
        }
 
471
        newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
 
472
        if (!newbuf) {
 
473
            ret = ENOMEM;
 
474
            goto done;
 
475
        }
 
476
        buffer = newbuf;
 
477
        goto again;
 
478
 
 
479
    case NSS_STATUS_NOTFOUND:
 
480
 
 
481
        DEBUG(7, ("Group %s not found.\n", name));
 
482
        delete_group = true;
 
483
        break;
 
484
 
 
485
    case NSS_STATUS_SUCCESS:
 
486
 
 
487
        DEBUG(7, ("Group %s found: (%s, %d)\n",
 
488
                  name, grp->gr_name, grp->gr_gid));
 
489
 
 
490
        /* gid=0 is an invalid value */
 
491
        /* also check that the id is in the valid range for this domain */
 
492
        if (OUT_OF_ID_RANGE(grp->gr_gid, dom->id_min, dom->id_max)) {
 
493
 
 
494
                DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
 
495
                          name));
 
496
            delete_group = true;
 
497
            break;
 
498
        }
 
499
 
 
500
        DEBUG_GR_MEM(7, grp);
 
501
 
 
502
        if (grp->gr_mem && grp->gr_mem[0]) {
 
503
            members = sysdb_new_attrs(tmpctx);
 
504
            if (!members) {
 
505
                ret = ENOMEM;
 
506
                goto done;
 
507
            }
 
508
            ret = sysdb_attrs_users_from_str_list(
 
509
                    members, SYSDB_MEMBER, dom->name,
 
510
                    (const char *const *)grp->gr_mem);
 
511
            if (ret) {
 
512
                goto done;
 
513
            }
 
514
        } else {
 
515
            members = NULL;
 
516
        }
 
517
 
 
518
        ret = sysdb_store_group(tmpctx, sysdb, dom,
 
519
                                grp->gr_name,
 
520
                                grp->gr_gid,
 
521
                                members,
 
522
                                ctx->entry_cache_timeout);
 
523
        if (ret) {
 
524
            goto done;
 
525
        }
 
526
        break;
 
527
 
 
528
    case NSS_STATUS_UNAVAIL:
 
529
        /* "remote" backend unavailable. Enter offline mode */
 
530
        ret = ENXIO;
 
531
        goto done;
 
532
 
 
533
    default:
 
534
        ret = EIO;
 
535
        goto done;
 
536
    }
 
537
 
 
538
    if (delete_group) {
 
539
        struct ldb_dn *dn;
 
540
 
 
541
        DEBUG(7, ("Group %s does not exist (or is invalid) on remote server,"
 
542
                  " deleting!\n", name));
 
543
 
 
544
        dn = sysdb_group_dn(sysdb, tmpctx, dom->name, name);
 
545
        if (!dn) {
 
546
            ret = ENOMEM;
 
547
            goto done;
 
548
        }
 
549
 
 
550
        ret = sysdb_delete_entry(sysdb, dn, true);
 
551
        if (ret) {
 
552
            goto done;
 
553
        }
 
554
    }
 
555
 
 
556
done:
 
557
    talloc_zfree(tmpctx);
 
558
    if (ret) {
 
559
        DEBUG(2, ("proxy -> getgrnam_r failed for '%s' <%d>\n",
 
560
                  name, status));
 
561
    }
 
562
    return ret;
 
563
}
 
564
 
 
565
/* =Getgrgid-wrapper======================================================*/
 
566
 
 
567
static int get_gr_gid(TALLOC_CTX *mem_ctx,
 
568
                      struct proxy_id_ctx *ctx,
 
569
                      struct sysdb_ctx *sysdb,
 
570
                      struct sss_domain_info *dom,
 
571
                      gid_t gid)
 
572
{
 
573
    TALLOC_CTX *tmpctx;
 
574
    struct group *grp;
 
575
    enum nss_status status;
 
576
    char *buffer;
 
577
    char *newbuf;
 
578
    size_t buflen;
 
579
    bool delete_group = false;
 
580
    struct sysdb_attrs *members;
 
581
    int ret;
 
582
 
 
583
    DEBUG(7, ("Searching group by gid (%d)\n", gid));
 
584
 
 
585
    tmpctx = talloc_new(mem_ctx);
 
586
    if (!tmpctx) {
 
587
        return ENOMEM;
 
588
    }
 
589
 
 
590
    grp = talloc(tmpctx, struct group);
 
591
    if (!grp) {
 
592
        ret = ENOMEM;
 
593
        DEBUG(1, ("proxy -> getgrgid_r failed for '%d': [%d] %s\n",
 
594
                  gid, ret, strerror(ret)));
 
595
        return ret;
 
596
    }
 
597
 
 
598
    buflen = DEFAULT_BUFSIZE;
 
599
    buffer = talloc_size(tmpctx, buflen);
 
600
    if (!buffer) {
 
601
        ret = ENOMEM;
 
602
        DEBUG(1, ("proxy -> getgrgid_r failed for '%d': [%d] %s\n",
 
603
                  gid, ret, strerror(ret)));
 
604
        return ret;
 
605
    }
 
606
 
 
607
again:
 
608
    /* always zero out the group structure */
 
609
    memset(grp, 0, sizeof(struct group));
 
610
 
 
611
    status = ctx->ops.getgrgid_r(gid, grp, buffer, buflen, &ret);
 
612
 
 
613
    switch (status) {
 
614
    case NSS_STATUS_TRYAGAIN:
 
615
        /* buffer too small ? */
 
616
        if (buflen < MAX_BUF_SIZE) {
 
617
            buflen *= 2;
 
618
        }
 
619
        if (buflen > MAX_BUF_SIZE) {
 
620
            buflen = MAX_BUF_SIZE;
 
621
        }
 
622
        newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
 
623
        if (!newbuf) {
 
624
            ret = ENOMEM;
 
625
            goto done;
 
626
        }
 
627
        buffer = newbuf;
 
628
        goto again;
 
629
 
 
630
    case NSS_STATUS_NOTFOUND:
 
631
 
 
632
        DEBUG(7, ("Group %d not found.\n", gid));
 
633
        delete_group = true;
 
634
        break;
 
635
 
 
636
    case NSS_STATUS_SUCCESS:
 
637
 
 
638
        DEBUG(7, ("Group %d found (%s, %d)\n",
 
639
                  gid, grp->gr_name, grp->gr_gid));
 
640
 
 
641
        /* gid=0 is an invalid value */
 
642
        /* also check that the id is in the valid range for this domain */
 
643
        if (OUT_OF_ID_RANGE(grp->gr_gid, dom->id_min, dom->id_max)) {
 
644
 
 
645
                DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
 
646
                          grp->gr_name));
 
647
            delete_group = true;
 
648
            break;
 
649
        }
 
650
 
 
651
        DEBUG_GR_MEM(7, grp);
 
652
 
 
653
        if (grp->gr_mem && grp->gr_mem[0]) {
 
654
            members = sysdb_new_attrs(tmpctx);
 
655
            if (!members) {
 
656
                ret = ENOMEM;
 
657
                goto done;
 
658
            }
 
659
            ret = sysdb_attrs_users_from_str_list(
 
660
                    members, SYSDB_MEMBER, dom->name,
 
661
                    (const char *const *)grp->gr_mem);
 
662
            if (ret) {
 
663
                goto done;
 
664
            }
 
665
        } else {
 
666
            members = NULL;
 
667
        }
 
668
 
 
669
        ret = sysdb_store_group(tmpctx, sysdb, dom,
 
670
                                grp->gr_name,
 
671
                                grp->gr_gid,
 
672
                                members,
 
673
                                ctx->entry_cache_timeout);
 
674
        if (ret) {
 
675
            goto done;
 
676
        }
 
677
        break;
 
678
 
 
679
    case NSS_STATUS_UNAVAIL:
 
680
        /* "remote" backend unavailable. Enter offline mode */
 
681
        ret = ENXIO;
 
682
        goto done;
 
683
 
 
684
    default:
 
685
        ret = EIO;
 
686
        goto done;
 
687
    }
 
688
 
 
689
    if (delete_group) {
 
690
 
 
691
        DEBUG(7, ("Group %d does not exist (or is invalid) on remote server,"
 
692
                  " deleting!\n", gid));
 
693
 
 
694
        ret = sysdb_delete_group(tmpctx, sysdb, dom, NULL, gid);
 
695
        if (ret) {
 
696
            goto done;
 
697
        }
 
698
    }
 
699
 
 
700
done:
 
701
    talloc_zfree(tmpctx);
 
702
    if (ret) {
 
703
        DEBUG(2, ("proxy -> getgrgid_r failed for '%d' <%d>\n",
 
704
                  gid, status));
 
705
    }
 
706
    return ret;
 
707
}
 
708
 
 
709
/* =Getgrent-wrapper======================================================*/
 
710
 
 
711
static int enum_groups(TALLOC_CTX *mem_ctx,
 
712
                       struct proxy_id_ctx *ctx,
 
713
                       struct sysdb_ctx *sysdb,
 
714
                       struct sss_domain_info *dom)
 
715
{
 
716
    TALLOC_CTX *tmpctx;
 
717
    bool in_transaction = false;
 
718
    struct group *grp;
 
719
    enum nss_status status;
 
720
    size_t buflen;
 
721
    char *buffer;
 
722
    struct sysdb_attrs *members;
 
723
    char *newbuf;
 
724
    int ret;
 
725
 
 
726
    DEBUG(7, ("Enumerating groups\n"));
 
727
 
 
728
    tmpctx = talloc_new(mem_ctx);
 
729
    if (!tmpctx) {
 
730
        return ENOMEM;
 
731
    }
 
732
 
 
733
    grp = talloc(tmpctx, struct group);
 
734
    if (!grp) {
 
735
        ret = ENOMEM;
 
736
        goto done;
 
737
    }
 
738
 
 
739
    buflen = DEFAULT_BUFSIZE;
 
740
    buffer = talloc_size(tmpctx, buflen);
 
741
    if (!buffer) {
 
742
        ret = ENOMEM;
 
743
        goto done;
 
744
    }
 
745
 
 
746
    ret = sysdb_transaction_start(sysdb);
 
747
    if (ret) {
 
748
        goto done;
 
749
    }
 
750
    in_transaction = true;
 
751
 
 
752
    status = ctx->ops.setgrent();
 
753
    if (status != NSS_STATUS_SUCCESS) {
 
754
        ret = EIO;
 
755
        goto done;
 
756
    }
 
757
 
 
758
again:
 
759
    /* always zero out the grp structure */
 
760
    memset(grp, 0, sizeof(struct group));
 
761
 
 
762
    /* get entry */
 
763
    status = ctx->ops.getgrent_r(grp, buffer, buflen, &ret);
 
764
 
 
765
    switch (status) {
 
766
    case NSS_STATUS_TRYAGAIN:
 
767
        /* buffer too small ? */
 
768
        if (buflen < MAX_BUF_SIZE) {
 
769
            buflen *= 2;
 
770
        }
 
771
        if (buflen > MAX_BUF_SIZE) {
 
772
            buflen = MAX_BUF_SIZE;
 
773
        }
 
774
        newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
 
775
        if (!newbuf) {
 
776
            ret = ENOMEM;
 
777
            goto done;
 
778
        }
 
779
        buffer = newbuf;
 
780
        goto again;
 
781
 
 
782
    case NSS_STATUS_NOTFOUND:
 
783
 
 
784
        /* we are done here */
 
785
        DEBUG(7, ("Enumeration completed.\n"));
 
786
 
 
787
        ret = sysdb_transaction_commit(sysdb);
 
788
        in_transaction = false;
 
789
        break;
 
790
 
 
791
    case NSS_STATUS_SUCCESS:
 
792
 
 
793
        DEBUG(7, ("Group found (%s, %d)\n",
 
794
                  grp->gr_name, grp->gr_gid));
 
795
 
 
796
        /* gid=0 is an invalid value */
 
797
        /* also check that the id is in the valid range for this domain */
 
798
        if (OUT_OF_ID_RANGE(grp->gr_gid, dom->id_min, dom->id_max)) {
 
799
 
 
800
                DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
 
801
                          grp->gr_name));
 
802
 
 
803
            goto again; /* skip */
 
804
        }
 
805
 
 
806
        DEBUG_GR_MEM(7, grp);
 
807
 
 
808
        if (grp->gr_mem && grp->gr_mem[0]) {
 
809
            members = sysdb_new_attrs(tmpctx);
 
810
            if (!members) {
 
811
                ret = ENOMEM;
 
812
                goto done;
 
813
            }
 
814
            ret = sysdb_attrs_users_from_str_list(
 
815
                    members, SYSDB_MEMBER, dom->name,
 
816
                    (const char *const *)grp->gr_mem);
 
817
            if (ret) {
 
818
                goto done;
 
819
            }
 
820
        } else {
 
821
            members = NULL;
 
822
        }
 
823
 
 
824
        ret = sysdb_store_group(tmpctx, sysdb, dom,
 
825
                                grp->gr_name,
 
826
                                grp->gr_gid,
 
827
                                members,
 
828
                                ctx->entry_cache_timeout);
 
829
        if (ret) {
 
830
            /* Do not fail completely on errors.
 
831
             * Just report the failure to save and go on */
 
832
            DEBUG(2, ("Failed to store group. Ignoring.\n"));
 
833
        }
 
834
        goto again; /* next */
 
835
 
 
836
    case NSS_STATUS_UNAVAIL:
 
837
        /* "remote" backend unavailable. Enter offline mode */
 
838
        ret = ENXIO;
 
839
        break;
 
840
 
 
841
    default:
 
842
        ret = EIO;
 
843
        DEBUG(2, ("proxy -> getgrent_r failed (%d)[%s]\n",
 
844
                  ret, strerror(ret)));
 
845
        break;
 
846
    }
 
847
 
 
848
done:
 
849
    talloc_zfree(tmpctx);
 
850
    if (in_transaction) {
 
851
        sysdb_transaction_cancel(sysdb);
 
852
    }
 
853
    ctx->ops.endgrent();
 
854
    return ret;
 
855
}
 
856
 
 
857
 
 
858
/* =Initgroups-wrapper====================================================*/
 
859
 
 
860
static int get_initgr_groups_process(TALLOC_CTX *memctx,
 
861
                                     struct proxy_id_ctx *ctx,
 
862
                                     struct sysdb_ctx *sysdb,
 
863
                                     struct sss_domain_info *dom,
 
864
                                     struct passwd *pwd);
 
865
 
 
866
static int get_initgr(TALLOC_CTX *mem_ctx,
 
867
                      struct proxy_id_ctx *ctx,
 
868
                      struct sysdb_ctx *sysdb,
 
869
                      struct sss_domain_info *dom,
 
870
                      const char *name)
 
871
{
 
872
    TALLOC_CTX *tmpctx;
 
873
    bool in_transaction = false;
 
874
    struct passwd *pwd;
 
875
    enum nss_status status;
 
876
    char *buffer;
 
877
    size_t buflen;
 
878
    int ret;
 
879
 
 
880
    tmpctx = talloc_new(mem_ctx);
 
881
    if (!tmpctx) {
 
882
        return ENOMEM;
 
883
    }
 
884
 
 
885
    pwd = talloc_zero(tmpctx, struct passwd);
 
886
    if (!pwd) {
 
887
        ret = ENOMEM;
 
888
        goto done;
 
889
    }
 
890
 
 
891
    buflen = DEFAULT_BUFSIZE;
 
892
    buffer = talloc_size(tmpctx, buflen);
 
893
    if (!buffer) {
 
894
        ret = ENOMEM;
 
895
        goto done;
 
896
    }
 
897
 
 
898
    ret = sysdb_transaction_start(sysdb);
 
899
    if (ret) {
 
900
        goto done;
 
901
    }
 
902
    in_transaction = true;
 
903
 
 
904
    /* FIXME: should we move this call outside the transaction to keep the
 
905
     * transaction as short as possible ? */
 
906
    status = ctx->ops.getpwnam_r(name, pwd, buffer, buflen, &ret);
 
907
 
 
908
    switch (status) {
 
909
    case NSS_STATUS_NOTFOUND:
 
910
 
 
911
        DEBUG(7, ("User %s not found.\n", name));
 
912
        ret = delete_user(tmpctx, sysdb, dom, name);
 
913
        if (ret) {
 
914
            goto done;
 
915
        }
 
916
        break;
 
917
 
 
918
    case NSS_STATUS_SUCCESS:
 
919
 
 
920
        /* uid=0 or gid=0 are invalid values */
 
921
        /* also check that the id is in the valid range for this domain */
 
922
        if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
 
923
            OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
 
924
 
 
925
            DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
 
926
                      name));
 
927
            ret = delete_user(tmpctx, sysdb, dom, name);
 
928
            break;
 
929
        }
 
930
 
 
931
        ret = sysdb_store_user(tmpctx, sysdb, dom,
 
932
                               pwd->pw_name,
 
933
                               pwd->pw_passwd,
 
934
                               pwd->pw_uid,
 
935
                               pwd->pw_gid,
 
936
                               pwd->pw_gecos,
 
937
                               pwd->pw_dir,
 
938
                               pwd->pw_shell,
 
939
                               NULL, NULL,
 
940
                               ctx->entry_cache_timeout);
 
941
        if (ret) {
 
942
            goto done;
 
943
        }
 
944
 
 
945
        ret = get_initgr_groups_process(tmpctx, ctx, sysdb, dom, pwd);
 
946
        if (ret == EOK) {
 
947
            ret = sysdb_transaction_commit(sysdb);
 
948
            in_transaction = true;
 
949
        }
 
950
        break;
 
951
 
 
952
    case NSS_STATUS_UNAVAIL:
 
953
        /* "remote" backend unavailable. Enter offline mode */
 
954
        ret = ENXIO;
 
955
        break;
 
956
 
 
957
    default:
 
958
        DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n",
 
959
                  name, status));
 
960
        ret = EIO;
 
961
        break;
 
962
    }
 
963
 
 
964
done:
 
965
    talloc_zfree(tmpctx);
 
966
    if (in_transaction) {
 
967
        sysdb_transaction_cancel(sysdb);
 
968
    }
 
969
    return ret;
 
970
}
 
971
 
 
972
static int get_initgr_groups_process(TALLOC_CTX *memctx,
 
973
                                     struct proxy_id_ctx *ctx,
 
974
                                     struct sysdb_ctx *sysdb,
 
975
                                     struct sss_domain_info *dom,
 
976
                                     struct passwd *pwd)
 
977
{
 
978
    enum nss_status status;
 
979
    long int limit;
 
980
    long int size;
 
981
    long int num;
 
982
    long int num_gids;
 
983
    gid_t *gids;
 
984
    int ret;
 
985
    int i;
 
986
 
 
987
    num_gids = 0;
 
988
    limit = 4096;
 
989
    num = 4096;
 
990
    size = num*sizeof(gid_t);
 
991
    gids = talloc_size(memctx, size);
 
992
    if (!gids) {
 
993
        return ENOMEM;
 
994
    }
 
995
 
 
996
again:
 
997
    /* FIXME: should we move this call outside the transaction to keep the
 
998
     * transaction as short as possible ? */
 
999
    status = ctx->ops.initgroups_dyn(pwd->pw_name, pwd->pw_gid, &num_gids,
 
1000
                                     &num, &gids, limit, &ret);
 
1001
    switch (status) {
 
1002
    case NSS_STATUS_TRYAGAIN:
 
1003
        /* buffer too small ? */
 
1004
        if (size < MAX_BUF_SIZE) {
 
1005
            num *= 2;
 
1006
            size = num*sizeof(gid_t);
 
1007
        }
 
1008
        if (size > MAX_BUF_SIZE) {
 
1009
            size = MAX_BUF_SIZE;
 
1010
            num = size/sizeof(gid_t);
 
1011
        }
 
1012
        limit = num;
 
1013
        gids = talloc_realloc_size(memctx, gids, size);
 
1014
        if (!gids) {
 
1015
            return ENOMEM;
 
1016
        }
 
1017
        goto again; /* retry with more memory */
 
1018
 
 
1019
    case NSS_STATUS_SUCCESS:
 
1020
        DEBUG(4, ("User [%s] appears to be member of %lu groups\n",
 
1021
                  pwd->pw_name, num_gids));
 
1022
 
 
1023
        for (i = 0; i < num_gids; i++) {
 
1024
            ret = get_gr_gid(memctx, ctx, sysdb, dom, gids[i]);
 
1025
            if (ret) {
 
1026
                return ret;
 
1027
            }
 
1028
        }
 
1029
        break;
 
1030
 
 
1031
    default:
 
1032
        ret = EIO;
 
1033
        DEBUG(2, ("proxy -> initgroups_dyn failed (%d)[%s]\n",
 
1034
                  ret, strerror(ret)));
 
1035
        break;
 
1036
    }
 
1037
 
 
1038
    return ret;
 
1039
}
 
1040
 
 
1041
/* =Proxy_Id-Functions====================================================*/
 
1042
 
 
1043
void proxy_get_account_info(struct be_req *breq)
 
1044
{
 
1045
    struct be_acct_req *ar;
 
1046
    struct proxy_id_ctx *ctx;
 
1047
    struct sysdb_ctx *sysdb;
 
1048
    struct sss_domain_info *domain;
 
1049
    uid_t uid;
 
1050
    gid_t gid;
 
1051
    int ret;
 
1052
    char *endptr;
 
1053
 
 
1054
    ar = talloc_get_type(breq->req_data, struct be_acct_req);
 
1055
    ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data,
 
1056
                          struct proxy_id_ctx);
 
1057
    sysdb = breq->be_ctx->sysdb;
 
1058
    domain = breq->be_ctx->domain;
 
1059
 
 
1060
    if (be_is_offline(breq->be_ctx)) {
 
1061
        return proxy_reply(breq, DP_ERR_OFFLINE, EAGAIN, "Offline");
 
1062
    }
 
1063
 
 
1064
    /* for now we support only core attrs */
 
1065
    if (ar->attr_type != BE_ATTR_CORE) {
 
1066
        return proxy_reply(breq, DP_ERR_FATAL, EINVAL, "Invalid attr type");
 
1067
    }
 
1068
 
 
1069
    switch (ar->entry_type & 0xFFF) {
 
1070
    case BE_REQ_USER: /* user */
 
1071
        switch (ar->filter_type) {
 
1072
        case BE_FILTER_ENUM:
 
1073
            ret = enum_users(breq, ctx, sysdb, domain);
 
1074
            break;
 
1075
 
 
1076
        case BE_FILTER_NAME:
 
1077
            ret = get_pw_name(breq, ctx, sysdb, domain, ar->filter_value);
 
1078
            break;
 
1079
 
 
1080
        case BE_FILTER_IDNUM:
 
1081
            uid = (uid_t) strtouint32(ar->filter_value, &endptr, 0);
 
1082
            if (errno || *endptr || (ar->filter_value == endptr)) {
 
1083
                return proxy_reply(breq, DP_ERR_FATAL,
 
1084
                                   EINVAL, "Invalid attr type");
 
1085
            }
 
1086
            ret = get_pw_uid(breq, ctx, sysdb, domain, uid);
 
1087
            break;
 
1088
        default:
 
1089
            return proxy_reply(breq, DP_ERR_FATAL,
 
1090
                               EINVAL, "Invalid filter type");
 
1091
        }
 
1092
        break;
 
1093
 
 
1094
    case BE_REQ_GROUP: /* group */
 
1095
        switch (ar->filter_type) {
 
1096
        case BE_FILTER_ENUM:
 
1097
            ret = enum_groups(breq, ctx, sysdb, domain);
 
1098
            break;
 
1099
        case BE_FILTER_NAME:
 
1100
            ret = get_gr_name(breq, ctx, sysdb, domain, ar->filter_value);
 
1101
            break;
 
1102
        case BE_FILTER_IDNUM:
 
1103
            gid = (gid_t) strtouint32(ar->filter_value, &endptr, 0);
 
1104
            if (errno || *endptr || (ar->filter_value == endptr)) {
 
1105
                return proxy_reply(breq, DP_ERR_FATAL,
 
1106
                                   EINVAL, "Invalid attr type");
 
1107
            }
 
1108
            ret = get_gr_gid(breq, ctx, sysdb, domain, gid);
 
1109
            break;
 
1110
        default:
 
1111
            return proxy_reply(breq, DP_ERR_FATAL,
 
1112
                               EINVAL, "Invalid filter type");
 
1113
        }
 
1114
        break;
 
1115
 
 
1116
    case BE_REQ_INITGROUPS: /* init groups for user */
 
1117
        if (ar->filter_type != BE_FILTER_NAME) {
 
1118
            return proxy_reply(breq, DP_ERR_FATAL,
 
1119
                               EINVAL, "Invalid filter type");
 
1120
        }
 
1121
        if (ctx->ops.initgroups_dyn == NULL) {
 
1122
            return proxy_reply(breq, DP_ERR_FATAL,
 
1123
                               ENODEV, "Initgroups call not supported");
 
1124
        }
 
1125
        ret = get_initgr(breq, ctx, sysdb, domain, ar->filter_value);
 
1126
        break;
 
1127
 
 
1128
    case BE_REQ_NETGROUP:
 
1129
        if (ar->filter_type != BE_FILTER_NAME) {
 
1130
            return proxy_reply(breq, DP_ERR_FATAL,
 
1131
                               EINVAL, "Invalid filter type");
 
1132
        }
 
1133
        if (ctx->ops.setnetgrent == NULL || ctx->ops.getnetgrent_r == NULL ||
 
1134
            ctx->ops.endnetgrent == NULL) {
 
1135
            return proxy_reply(breq, DP_ERR_FATAL,
 
1136
                               ENODEV, "Netgroups are not supported");
 
1137
        }
 
1138
 
 
1139
        ret = get_netgroup(ctx, sysdb, domain, ar->filter_value);
 
1140
        break;
 
1141
 
 
1142
    default: /*fail*/
 
1143
        return proxy_reply(breq, DP_ERR_FATAL,
 
1144
                           EINVAL, "Invalid request type");
 
1145
    }
 
1146
 
 
1147
    if (ret) {
 
1148
        if (ret == ENXIO) {
 
1149
            DEBUG(2, ("proxy returned UNAVAIL error, going offline!\n"));
 
1150
            be_mark_offline(breq->be_ctx);
 
1151
        }
 
1152
        proxy_reply(breq, DP_ERR_FATAL, ret, NULL);
 
1153
        return;
 
1154
    }
 
1155
    proxy_reply(breq, DP_ERR_OK, EOK, NULL);
 
1156
}