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