1#define _GNU_SOURCE
2#include "nscd.h"
3#include "pwf.h"
4#include <byteswap.h>
5#include <errno.h>
6#include <grp.h>
7#include <limits.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12int getgrouplist(const char* user, gid_t gid, gid_t* groups, int* ngroups) {
13    int rv, nlim, ret = -1;
14    ssize_t i, n = 1;
15    struct group gr;
16    struct group* res;
17    FILE* f;
18    int swap = 0;
19    int32_t resp[INITGR_LEN];
20    uint32_t* nscdbuf = 0;
21    char* buf = 0;
22    char** mem = 0;
23    size_t nmem = 0;
24    size_t size;
25    nlim = *ngroups;
26    if (nlim >= 1)
27        *groups++ = gid;
28
29    f = __nscd_query(GETINITGR, user, resp, sizeof resp, &swap);
30    if (!f)
31        goto cleanup;
32    if (resp[INITGRFOUND]) {
33        nscdbuf = calloc(resp[INITGRNGRPS], sizeof(uint32_t));
34        if (!nscdbuf)
35            goto cleanup;
36        if (!fread(nscdbuf, sizeof(*nscdbuf) * resp[INITGRNGRPS], 1, f)) {
37            if (!ferror(f))
38                errno = EIO;
39            goto cleanup;
40        }
41        if (swap) {
42            for (i = 0; i < resp[INITGRNGRPS]; i++)
43                nscdbuf[i] = bswap_32(nscdbuf[i]);
44        }
45    }
46    fclose(f);
47
48    f = fopen("/etc/group", "rbe");
49    if (!f && errno != ENOENT && errno != ENOTDIR)
50        goto cleanup;
51
52    if (f) {
53        while (!(rv = __getgrent_a(f, &gr, &buf, &size, &mem, &nmem, &res)) && res) {
54            if (nscdbuf)
55                for (i = 0; i < resp[INITGRNGRPS]; i++) {
56                    if (nscdbuf[i] == gr.gr_gid)
57                        nscdbuf[i] = gid;
58                }
59            for (i = 0; gr.gr_mem[i] && strcmp(user, gr.gr_mem[i]); i++)
60                ;
61            if (!gr.gr_mem[i])
62                continue;
63            if (++n <= nlim)
64                *groups++ = gr.gr_gid;
65        }
66        if (rv) {
67            errno = rv;
68            goto cleanup;
69        }
70    }
71    if (nscdbuf) {
72        for (i = 0; i < resp[INITGRNGRPS]; i++) {
73            if (nscdbuf[i] != gid)
74                if (++n <= nlim)
75                    *groups++ = nscdbuf[i];
76        }
77    }
78
79    ret = n > nlim ? -1 : n;
80    *ngroups = n;
81
82cleanup:
83    if (f)
84        fclose(f);
85    free(nscdbuf);
86    free(buf);
87    free(mem);
88    return ret;
89}
90