• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
itoa(char * p,uint32_t x)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 
__getgr_a(const char * name,gid_t gid,struct group * gr,char ** buf,size_t * size,char *** mem,size_t * nmem,struct group ** res)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 
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 
160 cleanup_f:
161 		fclose(f);
162 		goto done;
163 	}
164 
165 done:
166 	pthread_setcancelstate(cs, 0);
167 	if (rv) errno = rv;
168 	return rv;
169 }
170