1 /*
2 * System Security Services Daemon. NSS client interface
3 *
4 * Copyright (C) Simo Sorce 2007
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /* GROUP database NSS interface */
22
23 #include <nss.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <stdint.h>
29 #include <string.h>
30 #include "sss_cli.h"
31
32 static struct sss_nss_getgrent_data {
33 size_t len;
34 size_t ptr;
35 uint8_t *data;
36 } sss_nss_getgrent_data;
37
38 static void sss_nss_getgrent_data_clean(void) {
39
40 if (sss_nss_getgrent_data.data != NULL) {
41 free(sss_nss_getgrent_data.data);
42 sss_nss_getgrent_data.data = NULL;
43 }
44 sss_nss_getgrent_data.len = 0;
45 sss_nss_getgrent_data.ptr = 0;
46 }
47
48 /* GETGRNAM Request:
49 *
50 * 0-X: string with name
51 *
52 * GERTGRGID Request:
53 *
54 * 0-7: 32bit number with gid
55 *
56 * INITGROUPS Request:
57 *
58 * 0-3: 32bit number with gid
59 * 4-7: 32bit unsigned with max num of entries
60 *
61 * Replies:
62 *
63 * 0-3: 32bit unsigned number of results
64 * 4-7: 32bit unsigned (reserved/padding)
65 * For each result (64bit padded ?):
66 * 0-3: 32bit number gid
67 * 4-7: 32bit unsigned number of members
68 * 8-X: sequence of 0 terminated strings (name, passwd, mem..)
69 *
70 * FIXME: do we need to pad so that each result is 32 bit aligned ?
71 */
72 struct sss_nss_gr_rep {
73 struct group *result;
74 char *buffer;
75 size_t buflen;
76 };
77
78 static int sss_nss_getgr_readrep(struct sss_nss_gr_rep *pr,
79 uint8_t *buf, size_t *len)
80 {
81 size_t i, l, slen, ptmem, pad;
82 ssize_t dlen;
83 char *sbuf;
84 uint32_t mem_num;
85 uint32_t c;
86
87 if (*len < 11) { /* not enough space for data, bad packet */
88 return EBADMSG;
89 }
90
91 SAFEALIGN_COPY_UINT32(&c, buf, NULL);
92 pr->result->gr_gid = c;
93 SAFEALIGN_COPY_UINT32(&mem_num, buf+sizeof(uint32_t), NULL);
94
95 sbuf = (char *)&buf[8];
96 slen = *len - 8;
97 dlen = pr->buflen;
98
99 pr->result->gr_name = &(pr->buffer[0]);
100 i = 0;
101 while (slen > i && dlen > 0) {
102 pr->buffer[i] = sbuf[i];
103 if (pr->buffer[i] == '\0') break;
104 i++;
105 dlen--;
106 }
107 if (slen <= i) { /* premature end of buf */
108 return EBADMSG;
109 }
110 if (dlen <= 0) { /* not enough memory */
111 return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
112 }
113 i++;
114 dlen--;
115
116 pr->result->gr_passwd = &(pr->buffer[i]);
117 while (slen > i && dlen > 0) {
118 pr->buffer[i] = sbuf[i];
119 if (pr->buffer[i] == '\0') break;
120 i++;
121 dlen--;
122 }
123 if (slen <= i) { /* premature end of buf */
124 return EBADMSG;
125 }
126 if (dlen <= 0) { /* not enough memory */
127 return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
128 }
129 i++;
130 dlen--;
131
132 /* Make sure pr->buffer[i+pad] is 32 bit aligned */
133 pad = 0;
134 while((i + pad) % 4) {
135 pad++;
136 }
137
138 /* now members */
139 pr->result->gr_mem = (char **)&(pr->buffer[i+pad]);
140 ptmem = (sizeof(char *) * (mem_num + 1)) + pad;
141 if (ptmem > dlen) {
142 return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
143 }
144 dlen -= ptmem;
145 ptmem += i;
146 pr->result->gr_mem[mem_num] = NULL; /* terminate array */
147
148 for (l = 0; l < mem_num; l++) {
149 pr->result->gr_mem[l] = &(pr->buffer[ptmem]);
150 while ((slen > i) && (dlen > 0)) {
151 pr->buffer[ptmem] = sbuf[i];
152 if (pr->buffer[ptmem] == '\0') break;
153 i++;
154 dlen--;
155 ptmem++;
156 }
157 if (slen <= i) { /* premature end of buf */
158 return EBADMSG;
159 }
160 if (dlen <= 0) { /* not enough memory */
161 return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
162 }
163 i++;
164 dlen--;
165 ptmem++;
166 }
167
168 *len = slen -i;
169 return 0;
170 }
171
172 /* INITGROUP Reply:
173 *
174 * 0-3: 32bit unsigned number of results
175 * 4-7: 32bit unsigned (reserved/padding)
176 * For each result:
177 * 0-4: 32bit number with gid
178 */
179
180
181 enum nss_status _nss_sss_initgroups_dyn(const char *user, gid_t group,
182 long int *start, long int *size,
183 gid_t **groups, long int limit,
184 int *errnop)
185 {
186 struct sss_cli_req_data rd;
187 uint8_t *repbuf;
188 size_t replen;
189 enum nss_status nret;
190 uint32_t *rbuf;
191 uint32_t num_ret;
192 long int l, max_ret;
193
194 rd.len = strlen(user) +1;
195 rd.data = user;
196
197 nret = sss_nss_make_request(SSS_NSS_INITGR, &rd,
198 &repbuf, &replen, errnop);
199 if (nret != NSS_STATUS_SUCCESS) {
200 return nret;
201 }
202
203 /* no results if not found */
204 num_ret = ((uint32_t *)repbuf)[0];
205 if (num_ret == 0) {
206 free(repbuf);
207 return NSS_STATUS_NOTFOUND;
208 }
209 max_ret = num_ret;
210
211 /* check we have enough space in the buffer */
212 if ((*size - *start) < num_ret) {
213 long int newsize;
214 gid_t *newgroups;
215
216 newsize = *size + num_ret;
217 if ((limit > 0) && (newsize > limit)) {
218 newsize = limit;
219 max_ret = newsize - *start;
220 }
221
222 newgroups = (gid_t *)realloc((*groups), newsize * sizeof(**groups));
223 if (!newgroups) {
224 *errnop = ENOMEM;
225 free(repbuf);
226 return NSS_STATUS_TRYAGAIN;
227 }
228 *groups = newgroups;
229 *size = newsize;
230 }
231
232 rbuf = &((uint32_t *)repbuf)[2];
233 for (l = 0; l < max_ret; l++) {
234 (*groups)[*start] = rbuf[l];
235 *start += 1;
236 }
237
238 return NSS_STATUS_SUCCESS;
239 }
240
241
242 enum nss_status _nss_sss_getgrnam_r(const char *name, struct group *result,
243 char *buffer, size_t buflen, int *errnop)
244 {
245 struct sss_cli_req_data rd;
246 struct sss_nss_gr_rep grrep;
247 uint8_t *repbuf;
248 size_t replen, len;
249 enum nss_status nret;
250 int ret;
251
252 /* Caught once glibc passing in buffer == 0x0 */
253 if (!buffer || !buflen) return ERANGE;
254
255 rd.len = strlen(name) + 1;
256 rd.data = name;
257
258 nret = sss_nss_make_request(SSS_NSS_GETGRNAM, &rd,
259 &repbuf, &replen, errnop);
260 if (nret != NSS_STATUS_SUCCESS) {
261 return nret;
262 }
263
264 grrep.result = result;
265 grrep.buffer = buffer;
266 grrep.buflen = buflen;
267
268 /* no results if not found */
269 if (((uint32_t *)repbuf)[0] == 0) {
270 free(repbuf);
271 return NSS_STATUS_NOTFOUND;
272 }
273
274 /* only 1 result is accepted for this function */
275 if (((uint32_t *)repbuf)[0] != 1) {
276 *errnop = EBADMSG;
277 return NSS_STATUS_TRYAGAIN;
278 }
279
280 len = replen - 8;
281 ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len);
282 free(repbuf);
283 if (ret) {
284 *errnop = ret;
285 return NSS_STATUS_TRYAGAIN;
286 }
287
288 return NSS_STATUS_SUCCESS;
289 }
290
291 enum nss_status _nss_sss_getgrgid_r(gid_t gid, struct group *result,
292 char *buffer, size_t buflen, int *errnop)
293 {
294 struct sss_cli_req_data rd;
295 struct sss_nss_gr_rep grrep;
296 uint8_t *repbuf;
297 size_t replen, len;
298 enum nss_status nret;
299 uint32_t group_gid;
300 int ret;
301
302 /* Caught once glibc passing in buffer == 0x0 */
303 if (!buffer || !buflen) return ERANGE;
304
305 group_gid = gid;
306 rd.len = sizeof(uint32_t);
307 rd.data = &group_gid;
308
Event alloc_arg: Calling allocation function "sss_nss_make_request" on "repbuf". [model]
Also see events: [leaked_storage] | |
309 nret = sss_nss_make_request(SSS_NSS_GETGRGID, &rd,
310 &repbuf, &replen, errnop);
At conditional (1): "nret != 1": Taking false branch.
|
311 if (nret != NSS_STATUS_SUCCESS) {
312 return nret;
313 }
314
315 grrep.result = result;
316 grrep.buffer = buffer;
317 grrep.buflen = buflen;
318
319 /* no results if not found */
At conditional (2): "(uint32_t*)repbuf[0] == 0U": Taking false branch.
|
320 if (((uint32_t *)repbuf)[0] == 0) {
321 free(repbuf);
322 return NSS_STATUS_NOTFOUND;
323 }
324
325 /* only 1 result is accepted for this function */
At conditional (3): "(uint32_t*)repbuf[0] != 1U": Taking true branch.
|
326 if (((uint32_t *)repbuf)[0] != 1) {
327 *errnop = EBADMSG;
Event leaked_storage: Variable "repbuf" going out of scope leaks the storage it points to.
Also see events: [alloc_arg] | |
328 return NSS_STATUS_TRYAGAIN;
329 }
330
331 len = replen - 8;
332 ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len);
333 free(repbuf);
334 if (ret) {
335 *errnop = ret;
336 return NSS_STATUS_TRYAGAIN;
337 }
338
339 return NSS_STATUS_SUCCESS;
340 }
341
342 enum nss_status _nss_sss_setgrent(void)
343 {
344 enum nss_status nret;
345 int errnop;
346
347 /* make sure we do not have leftovers, and release memory */
348 sss_nss_getgrent_data_clean();
349
350 nret = sss_nss_make_request(SSS_NSS_SETGRENT,
351 NULL, NULL, NULL, &errnop);
352 if (nret != NSS_STATUS_SUCCESS) {
353 errno = errnop;
354 return nret;
355 }
356
357 return NSS_STATUS_SUCCESS;
358 }
359
360 enum nss_status _nss_sss_getgrent_r(struct group *result,
361 char *buffer, size_t buflen, int *errnop)
362 {
363 struct sss_cli_req_data rd;
364 struct sss_nss_gr_rep grrep;
365 uint8_t *repbuf;
366 size_t replen;
367 enum nss_status nret;
368 uint32_t num_entries;
369 int ret;
370
371 /* Caught once glibc passing in buffer == 0x0 */
372 if (!buffer || !buflen) return ERANGE;
373
374 /* if there are leftovers return the next one */
375 if (sss_nss_getgrent_data.data != NULL &&
376 sss_nss_getgrent_data.ptr < sss_nss_getgrent_data.len) {
377
378 repbuf = (uint8_t *)sss_nss_getgrent_data.data +
379 sss_nss_getgrent_data.ptr;
380 replen = sss_nss_getgrent_data.len -
381 sss_nss_getgrent_data.ptr;
382
383 grrep.result = result;
384 grrep.buffer = buffer;
385 grrep.buflen = buflen;
386
387 ret = sss_nss_getgr_readrep(&grrep, repbuf, &replen);
388 if (ret) {
389 *errnop = ret;
390 return NSS_STATUS_TRYAGAIN;
391 }
392
393 /* advance buffer pointer */
394 sss_nss_getgrent_data.ptr = sss_nss_getgrent_data.len - replen;
395
396 return NSS_STATUS_SUCCESS;
397 }
398
399 /* release memory if any */
400 sss_nss_getgrent_data_clean();
401
402 /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */
403 num_entries = SSS_NSS_MAX_ENTRIES;
404 rd.len = sizeof(uint32_t);
405 rd.data = &num_entries;
406
407 nret = sss_nss_make_request(SSS_NSS_GETGRENT, &rd,
408 &repbuf, &replen, errnop);
409 if (nret != NSS_STATUS_SUCCESS) {
410 return nret;
411 }
412
413 /* no results if not found */
414 if ((((uint32_t *)repbuf)[0] == 0) || (replen - 8 == 0)) {
415 free(repbuf);
416 return NSS_STATUS_NOTFOUND;
417 }
418
419 sss_nss_getgrent_data.data = repbuf;
420 sss_nss_getgrent_data.len = replen;
421 sss_nss_getgrent_data.ptr = 8; /* skip metadata fields */
422
423 /* call again ourselves, this will return the first result */
424 return _nss_sss_getgrent_r(result, buffer, buflen, errnop);
425 }
426
427 enum nss_status _nss_sss_endgrent(void)
428 {
429 enum nss_status nret;
430 int errnop;
431
432 /* make sure we do not have leftovers, and release memory */
433 sss_nss_getgrent_data_clean();
434
435 nret = sss_nss_make_request(SSS_NSS_ENDGRENT,
436 NULL, NULL, NULL, &errnop);
437 if (nret != NSS_STATUS_SUCCESS) {
438 errno = errnop;
439 return nret;
440 }
441
442 return NSS_STATUS_SUCCESS;
443 }