master
  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
  8static 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
 20int __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
 67		if (!groupbuf[GRFOUND]) { rv = 0; goto cleanup_f; }
 68
 69		if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) {
 70			rv = EIO;
 71			goto cleanup_f;
 72		}
 73
 74		if (groupbuf[GRNAMELEN] > SIZE_MAX - groupbuf[GRPASSWDLEN]) {
 75			rv = ENOMEM;
 76			goto cleanup_f;
 77		}
 78		len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
 79
 80		for (i = 0; i < groupbuf[GRMEMCNT]; i++) {
 81			uint32_t name_len;
 82			if (fread(&name_len, sizeof name_len, 1, f) < 1) {
 83				rv = ferror(f) ? errno : EIO;
 84				goto cleanup_f;
 85			}
 86			if (swap) {
 87				name_len = bswap_32(name_len);
 88			}
 89			if (name_len > SIZE_MAX - grlist_len
 90			|| 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) mem[0][++i] = ptr+1;
131			mem[0][i] = 0;
132
133			if (i != groupbuf[GRMEMCNT]) {
134				rv = EIO;
135				goto cleanup_f;
136			}
137		} else {
138			mem[0][0] = 0;
139		}
140
141		gr->gr_name = *buf;
142		gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN];
143		gr->gr_gid = groupbuf[GRGID];
144		gr->gr_mem = *mem;
145
146		if (gr->gr_passwd[-1]
147		|| gr->gr_passwd[groupbuf[GRPASSWDLEN]-1]) {
148			rv = EIO;
149			goto cleanup_f;
150		}
151
152		if (name && strcmp(name, gr->gr_name)
153		|| !name && gid != gr->gr_gid) {
154			rv = EIO;
155			goto cleanup_f;
156		}
157
158		*res = gr;
159
160cleanup_f:
161		fclose(f);
162		goto done;
163	}
164
165done:
166	pthread_setcancelstate(cs, 0);
167	if (rv) errno = rv;
168	return rv;
169}