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