1 #include "nscd.h"
2 #include "pwf.h"
3 #include <byteswap.h>
4 #include <string.h>
5 #include <unistd.h>
6 
itoa(char * p,uint32_t x)7 static char* itoa(char* p, uint32_t x) {
8     // number of digits in a uint32_t + NUL
9     p += 11;
10     *--p = 0;
11     do {
12         *--p = '0' + x % 10;
13         x /= 10;
14     } while (x);
15     return p;
16 }
17 
__getgr_a(const char * name,gid_t gid,struct group * gr,char ** buf,size_t * size,char *** mem,size_t * nmem,struct group ** res)18 int __getgr_a(const char* name, gid_t gid, struct group* gr, char** buf, size_t* size, char*** mem,
19               size_t* nmem, struct group** res) {
20     FILE* f;
21     int rv = 0;
22 
23     *res = 0;
24 
25     f = fopen("/etc/group", "rbe");
26     if (!f) {
27         rv = errno;
28         goto done;
29     }
30 
31     while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) {
32         if ((name && !strcmp(name, (*res)->gr_name)) || (!name && (*res)->gr_gid == gid)) {
33             break;
34         }
35     }
36     fclose(f);
37 
38     if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
39         int32_t req = name ? GETGRBYNAME : GETGRBYGID;
40         int32_t i;
41         const char* key;
42         int32_t groupbuf[GR_LEN] = {};
43         size_t len = 0;
44         size_t grlist_len = 0;
45         char gidbuf[11] = {};
46         int swap = 0;
47         char* ptr;
48 
49         if (name) {
50             key = name;
51         } else {
52             if (gid > UINT32_MAX) {
53                 rv = 0;
54                 goto done;
55             }
56             key = itoa(gidbuf, gid);
57         }
58 
59         f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap);
60         if (!f) {
61             rv = errno;
62             goto done;
63         }
64 
65         if (!groupbuf[GRFOUND]) {
66             rv = 0;
67             goto cleanup_f;
68         }
69 
70         if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) {
71             rv = EIO;
72             goto cleanup_f;
73         }
74 
75         if (groupbuf[GRNAMELEN] > SIZE_MAX - groupbuf[GRPASSWDLEN]) {
76             rv = ENOMEM;
77             goto cleanup_f;
78         }
79         len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
80 
81         for (i = 0; i < groupbuf[GRMEMCNT]; i++) {
82             uint32_t name_len;
83             if (fread(&name_len, sizeof name_len, 1, f) < 1) {
84                 rv = ferror(f) ? errno : EIO;
85                 goto cleanup_f;
86             }
87             if (swap) {
88                 name_len = bswap_32(name_len);
89             }
90             if (name_len > SIZE_MAX - grlist_len || name_len > SIZE_MAX - len) {
91                 rv = ENOMEM;
92                 goto cleanup_f;
93             }
94             len += name_len;
95             grlist_len += name_len;
96         }
97 
98         if (len > *size || !*buf) {
99             char* tmp = realloc(*buf, len);
100             if (!tmp) {
101                 rv = errno;
102                 goto cleanup_f;
103             }
104             *buf = tmp;
105             *size = len;
106         }
107 
108         if (!fread(*buf, len, 1, f)) {
109             rv = ferror(f) ? errno : EIO;
110             goto cleanup_f;
111         }
112 
113         if (groupbuf[GRMEMCNT] + 1 > *nmem) {
114             if (groupbuf[GRMEMCNT] + 1 > SIZE_MAX / sizeof(char*)) {
115                 rv = ENOMEM;
116                 goto cleanup_f;
117             }
118             char** tmp = realloc(*mem, (groupbuf[GRMEMCNT] + 1) * sizeof(char*));
119             if (!tmp) {
120                 rv = errno;
121                 goto cleanup_f;
122             }
123             *mem = tmp;
124             *nmem = groupbuf[GRMEMCNT] + 1;
125         }
126 
127         if (groupbuf[GRMEMCNT]) {
128             mem[0][0] = *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
129             for (ptr = mem[0][0], i = 0; ptr != mem[0][0] + grlist_len; ptr++)
130                 if (!*ptr)
131                     mem[0][++i] = ptr + 1;
132             mem[0][i] = 0;
133 
134             if (i != groupbuf[GRMEMCNT]) {
135                 rv = EIO;
136                 goto cleanup_f;
137             }
138         } else {
139             mem[0][0] = 0;
140         }
141 
142         gr->gr_name = *buf;
143         gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN];
144         gr->gr_gid = groupbuf[GRGID];
145         gr->gr_mem = *mem;
146 
147         if (gr->gr_passwd[-1] || gr->gr_passwd[groupbuf[GRPASSWDLEN] - 1]) {
148             rv = EIO;
149             goto cleanup_f;
150         }
151 
152         if ((name && strcmp(name, gr->gr_name)) || (!name && gid != gr->gr_gid)) {
153             rv = EIO;
154             goto cleanup_f;
155         }
156 
157         *res = gr;
158 
159     cleanup_f:
160         fclose(f);
161         goto done;
162     }
163 
164 done:
165     if (rv)
166         errno = rv;
167     return rv;
168 }
169