Line data Source code
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 License
17 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /* GROUP database NSS interface */
21 :
22 : #include <nss.h>
23 : #include <errno.h>
24 : #include <sys/types.h>
25 : #include <unistd.h>
26 : #include <stdlib.h>
27 : #include <stdint.h>
28 : #include <string.h>
29 : #include <stdbool.h>
30 : #include "sss_cli.h"
31 : #include "nss_mc.h"
32 :
33 : static struct sss_nss_getgrent_data {
34 : size_t len;
35 : size_t ptr;
36 : uint8_t *data;
37 : } sss_nss_getgrent_data;
38 :
39 0 : static void sss_nss_getgrent_data_clean(void)
40 : {
41 0 : if (sss_nss_getgrent_data.data != NULL) {
42 0 : free(sss_nss_getgrent_data.data);
43 0 : sss_nss_getgrent_data.data = NULL;
44 : }
45 0 : sss_nss_getgrent_data.len = 0;
46 0 : sss_nss_getgrent_data.ptr = 0;
47 0 : }
48 :
49 : enum sss_nss_gr_type {
50 : GETGR_NONE,
51 : GETGR_NAME,
52 : GETGR_GID
53 : };
54 :
55 : static struct sss_nss_getgr_data {
56 : enum sss_nss_gr_type type;
57 : union {
58 : char *grname;
59 : gid_t gid;
60 : } id;
61 :
62 : uint8_t *repbuf;
63 : size_t replen;
64 : } sss_nss_getgr_data;
65 :
66 26 : static void sss_nss_getgr_data_clean(bool freebuf)
67 : {
68 26 : if (sss_nss_getgr_data.type == GETGR_NAME) {
69 0 : free(sss_nss_getgr_data.id.grname);
70 : }
71 26 : if (freebuf) {
72 26 : free(sss_nss_getgr_data.repbuf);
73 : }
74 26 : memset(&sss_nss_getgr_data, 0, sizeof(struct sss_nss_getgr_data));
75 26 : }
76 :
77 26 : static enum nss_status sss_nss_get_getgr_cache(const char *name, gid_t gid,
78 : enum sss_nss_gr_type type,
79 : uint8_t **repbuf,
80 : size_t *replen,
81 : int *errnop)
82 : {
83 26 : bool freebuf = true;
84 : enum nss_status status;
85 26 : int ret = 0;
86 :
87 26 : if (sss_nss_getgr_data.type != type) {
88 26 : status = NSS_STATUS_NOTFOUND;
89 26 : goto done;
90 : }
91 :
92 0 : switch (type) {
93 : case GETGR_NAME:
94 0 : ret = strcmp(name, sss_nss_getgr_data.id.grname);
95 0 : if (ret != 0) {
96 0 : status = NSS_STATUS_NOTFOUND;
97 0 : goto done;
98 : }
99 0 : break;
100 : case GETGR_GID:
101 0 : if (sss_nss_getgr_data.id.gid != gid) {
102 0 : status = NSS_STATUS_NOTFOUND;
103 0 : goto done;
104 : }
105 0 : break;
106 : default:
107 0 : status = NSS_STATUS_TRYAGAIN;
108 0 : ret = EINVAL;
109 0 : goto done;
110 : }
111 :
112 : /* ok we have it, remove from cache and pass back to the caller */
113 0 : *repbuf = sss_nss_getgr_data.repbuf;
114 0 : *replen = sss_nss_getgr_data.replen;
115 :
116 : /* prevent _clean() from freeing the buffer */
117 0 : freebuf = false;
118 0 : status = NSS_STATUS_SUCCESS;
119 :
120 : done:
121 26 : sss_nss_getgr_data_clean(freebuf);
122 26 : *errnop = ret;
123 26 : return status;
124 : }
125 :
126 : /* this function always takes ownership of repbuf and NULLs it before
127 : * returning */
128 0 : static void sss_nss_save_getgr_cache(const char *name, gid_t gid,
129 : enum sss_nss_gr_type type,
130 : uint8_t **repbuf, size_t replen)
131 : {
132 0 : int ret = 0;
133 :
134 0 : sss_nss_getgr_data.type = type;
135 0 : sss_nss_getgr_data.repbuf = *repbuf;
136 0 : sss_nss_getgr_data.replen = replen;
137 :
138 0 : switch (type) {
139 : case GETGR_NAME:
140 0 : if (name == NULL) {
141 0 : ret = EINVAL;
142 0 : goto done;
143 : }
144 0 : sss_nss_getgr_data.id.grname = strdup(name);
145 0 : if (!sss_nss_getgr_data.id.grname) {
146 0 : ret = ENOMEM;
147 0 : goto done;
148 : }
149 0 : break;
150 : case GETGR_GID:
151 0 : if (gid == 0) {
152 0 : ret = EINVAL;
153 0 : goto done;
154 : }
155 0 : sss_nss_getgr_data.id.gid = gid;
156 0 : break;
157 : default:
158 0 : ret = EINVAL;
159 0 : goto done;
160 : }
161 :
162 : done:
163 0 : if (ret) {
164 0 : sss_nss_getgr_data_clean(true);
165 : }
166 0 : *repbuf = NULL;
167 0 : }
168 :
169 : /* GETGRNAM Request:
170 : *
171 : * 0-X: string with name
172 : *
173 : * GERTGRGID Request:
174 : *
175 : * 0-7: 32bit number with gid
176 : *
177 : * INITGROUPS Request:
178 : *
179 : * 0-3: 32bit number with gid
180 : * 4-7: 32bit unsigned with max num of entries
181 : *
182 : * Replies:
183 : *
184 : * 0-3: 32bit unsigned number of results
185 : * 4-7: 32bit unsigned (reserved/padding)
186 : * For each result (64bit padded ?):
187 : * 0-3: 32bit number gid
188 : * 4-7: 32bit unsigned number of members
189 : * 8-X: sequence of 0 terminated strings (name, passwd, mem..)
190 : *
191 : * FIXME: do we need to pad so that each result is 32 bit aligned ?
192 : */
193 : struct sss_nss_gr_rep {
194 : struct group *result;
195 : char *buffer;
196 : size_t buflen;
197 : };
198 :
199 0 : static int sss_nss_getgr_readrep(struct sss_nss_gr_rep *pr,
200 : uint8_t *buf, size_t *len)
201 : {
202 : errno_t ret;
203 : size_t i, l, slen, ptmem, pad, dlen, glen;
204 : char *sbuf;
205 : uint32_t mem_num;
206 : uint32_t c;
207 :
208 0 : if (*len < 11) { /* not enough space for data, bad packet */
209 0 : return EBADMSG;
210 : }
211 :
212 0 : SAFEALIGN_COPY_UINT32(&c, buf, NULL);
213 0 : pr->result->gr_gid = c;
214 0 : SAFEALIGN_COPY_UINT32(&mem_num, buf+sizeof(uint32_t), NULL);
215 :
216 0 : sbuf = (char *)&buf[8];
217 0 : slen = *len - 8;
218 0 : dlen = pr->buflen;
219 :
220 0 : pr->result->gr_name = &(pr->buffer[0]);
221 0 : i = 0;
222 :
223 0 : ret = sss_readrep_copy_string(sbuf, &i,
224 : &slen, &dlen,
225 0 : &pr->result->gr_name,
226 : NULL);
227 0 : if (ret != EOK) return ret;
228 :
229 0 : pr->result->gr_passwd = &(pr->buffer[i]);
230 0 : ret = sss_readrep_copy_string(sbuf, &i,
231 : &slen, &dlen,
232 0 : &pr->result->gr_passwd,
233 : NULL);
234 0 : if (ret != EOK) return ret;
235 :
236 : /* Make sure pr->buffer[i+pad] is aligned to sizeof(char *) */
237 0 : pad = PADDING_SIZE(i, char *);
238 :
239 : /* now members */
240 0 : pr->result->gr_mem = DISCARD_ALIGN(&(pr->buffer[i+pad]), char **);
241 :
242 0 : ptmem = (sizeof(char *) * (mem_num + 1)) + pad;
243 0 : if (ptmem > dlen) {
244 0 : return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
245 : }
246 0 : dlen -= ptmem;
247 0 : ptmem += i;
248 0 : pr->result->gr_mem[mem_num] = NULL; /* terminate array */
249 :
250 0 : for (l = 0; l < mem_num; l++) {
251 0 : pr->result->gr_mem[l] = &(pr->buffer[ptmem]);
252 0 : ret = sss_readrep_copy_string(sbuf, &i,
253 : &slen, &dlen,
254 0 : &pr->result->gr_mem[l],
255 : &glen);
256 0 : if (ret != EOK) return ret;
257 :
258 0 : ptmem += glen + 1;
259 : }
260 :
261 0 : *len = slen -i;
262 0 : return 0;
263 : }
264 :
265 : /* INITGROUP Reply:
266 : *
267 : * 0-3: 32bit unsigned number of results
268 : * 4-7: 32bit unsigned (reserved/padding)
269 : * For each result:
270 : * 0-4: 32bit number with gid
271 : */
272 :
273 :
274 0 : enum nss_status _nss_sss_initgroups_dyn(const char *user, gid_t group,
275 : long int *start, long int *size,
276 : gid_t **groups, long int limit,
277 : int *errnop)
278 : {
279 : struct sss_cli_req_data rd;
280 : uint8_t *repbuf;
281 : size_t replen;
282 : enum nss_status nret;
283 0 : size_t buf_index = 0;
284 : size_t user_len;
285 : uint32_t num_ret;
286 : long int l, max_ret;
287 : int ret;
288 :
289 0 : ret = sss_strnlen(user, SSS_NAME_MAX, &user_len);
290 0 : if (ret != 0) {
291 0 : *errnop = EINVAL;
292 0 : return NSS_STATUS_NOTFOUND;
293 : }
294 :
295 0 : ret = sss_nss_mc_initgroups_dyn(user, user_len, group, start, size,
296 : groups, limit);
297 0 : switch (ret) {
298 : case 0:
299 0 : *errnop = 0;
300 0 : return NSS_STATUS_SUCCESS;
301 : case ERANGE:
302 0 : *errnop = ERANGE;
303 0 : return NSS_STATUS_TRYAGAIN;
304 : case ENOENT:
305 : /* fall through, we need to actively ask the parent
306 : * if no entry is found */
307 0 : break;
308 : default:
309 : /* if using the mmaped cache failed,
310 : * fall back to socket based comms */
311 0 : break;
312 : }
313 :
314 0 : rd.len = user_len + 1;
315 0 : rd.data = user;
316 :
317 0 : sss_nss_lock();
318 :
319 : /* previous thread might already initialize entry in mmap cache */
320 0 : ret = sss_nss_mc_initgroups_dyn(user, user_len, group, start, size,
321 : groups, limit);
322 0 : switch (ret) {
323 : case 0:
324 0 : *errnop = 0;
325 0 : nret = NSS_STATUS_SUCCESS;
326 0 : goto out;
327 : case ERANGE:
328 0 : *errnop = ERANGE;
329 0 : nret = NSS_STATUS_TRYAGAIN;
330 0 : goto out;
331 : case ENOENT:
332 : /* fall through, we need to actively ask the parent
333 : * if no entry is found */
334 0 : break;
335 : default:
336 : /* if using the mmaped cache failed,
337 : * fall back to socket based comms */
338 0 : break;
339 : }
340 :
341 0 : nret = sss_nss_make_request(SSS_NSS_INITGR, &rd,
342 : &repbuf, &replen, errnop);
343 0 : if (nret != NSS_STATUS_SUCCESS) {
344 0 : goto out;
345 : }
346 :
347 : /* no results if not found */
348 0 : SAFEALIGN_COPY_UINT32(&num_ret, repbuf, NULL);
349 0 : if (num_ret == 0) {
350 0 : free(repbuf);
351 0 : nret = NSS_STATUS_NOTFOUND;
352 0 : goto out;
353 : }
354 0 : max_ret = num_ret;
355 :
356 : /* check we have enough space in the buffer */
357 0 : if ((*size - *start) < num_ret) {
358 : long int newsize;
359 : gid_t *newgroups;
360 :
361 0 : newsize = *size + num_ret;
362 0 : if ((limit > 0) && (newsize > limit)) {
363 0 : newsize = limit;
364 0 : max_ret = newsize - *start;
365 : }
366 :
367 0 : newgroups = (gid_t *)realloc((*groups), newsize * sizeof(**groups));
368 0 : if (!newgroups) {
369 0 : *errnop = ENOMEM;
370 0 : free(repbuf);
371 0 : nret = NSS_STATUS_TRYAGAIN;
372 0 : goto out;
373 : }
374 0 : *groups = newgroups;
375 0 : *size = newsize;
376 : }
377 :
378 : /* Skip first two 32 bit values (number of results and
379 : * reserved padding) */
380 0 : buf_index = 2 * sizeof(uint32_t);
381 :
382 0 : for (l = 0; l < max_ret; l++) {
383 0 : SAFEALIGN_COPY_UINT32(&(*groups)[*start], repbuf + buf_index,
384 : &buf_index);
385 0 : *start += 1;
386 : }
387 :
388 0 : free(repbuf);
389 0 : nret = NSS_STATUS_SUCCESS;
390 :
391 : out:
392 0 : sss_nss_unlock();
393 0 : return nret;
394 : }
395 :
396 :
397 24 : enum nss_status _nss_sss_getgrnam_r(const char *name, struct group *result,
398 : char *buffer, size_t buflen, int *errnop)
399 : {
400 : struct sss_cli_req_data rd;
401 : struct sss_nss_gr_rep grrep;
402 : uint8_t *repbuf;
403 : size_t replen, len, name_len;
404 : uint32_t num_results;
405 : enum nss_status nret;
406 : int ret;
407 :
408 : /* Caught once glibc passing in buffer == 0x0 */
409 24 : if (!buffer || !buflen) {
410 0 : *errnop = ERANGE;
411 0 : return NSS_STATUS_TRYAGAIN;
412 : }
413 :
414 24 : ret = sss_strnlen(name, SSS_NAME_MAX, &name_len);
415 24 : if (ret != 0) {
416 0 : *errnop = EINVAL;
417 0 : return NSS_STATUS_NOTFOUND;
418 : }
419 :
420 24 : ret = sss_nss_mc_getgrnam(name, name_len, result, buffer, buflen);
421 24 : switch (ret) {
422 : case 0:
423 0 : *errnop = 0;
424 0 : return NSS_STATUS_SUCCESS;
425 : case ERANGE:
426 0 : *errnop = ERANGE;
427 0 : return NSS_STATUS_TRYAGAIN;
428 : case ENOENT:
429 : /* fall through, we need to actively ask the parent
430 : * if no entry is found */
431 24 : break;
432 : default:
433 : /* if using the mmaped cache failed,
434 : * fall back to socket based comms */
435 0 : break;
436 : }
437 :
438 24 : rd.len = name_len + 1;
439 24 : rd.data = name;
440 :
441 24 : sss_nss_lock();
442 :
443 : /* previous thread might already initialize entry in mmap cache */
444 24 : ret = sss_nss_mc_getgrnam(name, name_len, result, buffer, buflen);
445 24 : switch (ret) {
446 : case 0:
447 0 : *errnop = 0;
448 0 : nret = NSS_STATUS_SUCCESS;
449 0 : goto out;
450 : case ERANGE:
451 0 : *errnop = ERANGE;
452 0 : nret = NSS_STATUS_TRYAGAIN;
453 0 : goto out;
454 : case ENOENT:
455 : /* fall through, we need to actively ask the parent
456 : * if no entry is found */
457 24 : break;
458 : default:
459 : /* if using the mmaped cache failed,
460 : * fall back to socket based comms */
461 0 : break;
462 : }
463 :
464 24 : nret = sss_nss_get_getgr_cache(name, 0, GETGR_NAME,
465 : &repbuf, &replen, errnop);
466 24 : if (nret == NSS_STATUS_NOTFOUND) {
467 24 : nret = sss_nss_make_request(SSS_NSS_GETGRNAM, &rd,
468 : &repbuf, &replen, errnop);
469 : }
470 24 : if (nret != NSS_STATUS_SUCCESS) {
471 24 : goto out;
472 : }
473 :
474 0 : grrep.result = result;
475 0 : grrep.buffer = buffer;
476 0 : grrep.buflen = buflen;
477 :
478 : /* Get number of results from repbuf. */
479 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
480 :
481 : /* no results if not found */
482 0 : if (num_results == 0) {
483 0 : free(repbuf);
484 0 : nret = NSS_STATUS_NOTFOUND;
485 0 : goto out;
486 : }
487 :
488 : /* only 1 result is accepted for this function */
489 0 : if (num_results != 1) {
490 0 : *errnop = EBADMSG;
491 0 : free(repbuf);
492 0 : nret = NSS_STATUS_TRYAGAIN;
493 0 : goto out;
494 : }
495 :
496 0 : len = replen - 8;
497 0 : ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len);
498 0 : if (ret == ERANGE) {
499 0 : sss_nss_save_getgr_cache(name, 0, GETGR_NAME, &repbuf, replen);
500 : } else {
501 0 : free(repbuf);
502 : }
503 0 : if (ret) {
504 0 : *errnop = ret;
505 0 : nret = NSS_STATUS_TRYAGAIN;
506 0 : goto out;
507 : }
508 :
509 0 : nret = NSS_STATUS_SUCCESS;
510 :
511 : out:
512 24 : sss_nss_unlock();
513 24 : return nret;
514 : }
515 :
516 2 : enum nss_status _nss_sss_getgrgid_r(gid_t gid, struct group *result,
517 : char *buffer, size_t buflen, int *errnop)
518 : {
519 : struct sss_cli_req_data rd;
520 : struct sss_nss_gr_rep grrep;
521 : uint8_t *repbuf;
522 : size_t replen, len;
523 : uint32_t num_results;
524 : enum nss_status nret;
525 : uint32_t group_gid;
526 : int ret;
527 :
528 : /* Caught once glibc passing in buffer == 0x0 */
529 2 : if (!buffer || !buflen) return ERANGE;
530 :
531 2 : ret = sss_nss_mc_getgrgid(gid, result, buffer, buflen);
532 2 : switch (ret) {
533 : case 0:
534 0 : *errnop = 0;
535 0 : return NSS_STATUS_SUCCESS;
536 : case ERANGE:
537 0 : *errnop = ERANGE;
538 0 : return NSS_STATUS_TRYAGAIN;
539 : case ENOENT:
540 : /* fall through, we need to actively ask the parent
541 : * if no entry is found */
542 2 : break;
543 : default:
544 : /* if using the mmaped cache failed,
545 : * fall back to socket based comms */
546 0 : break;
547 : }
548 :
549 2 : group_gid = gid;
550 2 : rd.len = sizeof(uint32_t);
551 2 : rd.data = &group_gid;
552 :
553 2 : sss_nss_lock();
554 :
555 : /* previous thread might already initialize entry in mmap cache */
556 2 : ret = sss_nss_mc_getgrgid(gid, result, buffer, buflen);
557 2 : switch (ret) {
558 : case 0:
559 0 : *errnop = 0;
560 0 : nret = NSS_STATUS_SUCCESS;
561 0 : goto out;
562 : case ERANGE:
563 0 : *errnop = ERANGE;
564 0 : nret = NSS_STATUS_TRYAGAIN;
565 0 : goto out;
566 : case ENOENT:
567 : /* fall through, we need to actively ask the parent
568 : * if no entry is found */
569 2 : break;
570 : default:
571 : /* if using the mmaped cache failed,
572 : * fall back to socket based comms */
573 0 : break;
574 : }
575 :
576 2 : nret = sss_nss_get_getgr_cache(NULL, gid, GETGR_GID,
577 : &repbuf, &replen, errnop);
578 2 : if (nret == NSS_STATUS_NOTFOUND) {
579 2 : nret = sss_nss_make_request(SSS_NSS_GETGRGID, &rd,
580 : &repbuf, &replen, errnop);
581 : }
582 2 : if (nret != NSS_STATUS_SUCCESS) {
583 2 : goto out;
584 : }
585 :
586 0 : grrep.result = result;
587 0 : grrep.buffer = buffer;
588 0 : grrep.buflen = buflen;
589 :
590 : /* Get number of results from repbuf. */
591 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
592 :
593 : /* no results if not found */
594 0 : if (num_results == 0) {
595 0 : free(repbuf);
596 0 : nret = NSS_STATUS_NOTFOUND;
597 0 : goto out;
598 : }
599 :
600 : /* only 1 result is accepted for this function */
601 0 : if (num_results != 1) {
602 0 : *errnop = EBADMSG;
603 0 : free(repbuf);
604 0 : nret = NSS_STATUS_TRYAGAIN;
605 0 : goto out;
606 : }
607 :
608 0 : len = replen - 8;
609 0 : ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len);
610 0 : if (ret == ERANGE) {
611 0 : sss_nss_save_getgr_cache(NULL, gid, GETGR_GID, &repbuf, replen);
612 : } else {
613 0 : free(repbuf);
614 : }
615 0 : if (ret) {
616 0 : *errnop = ret;
617 0 : nret = NSS_STATUS_TRYAGAIN;
618 0 : goto out;
619 : }
620 :
621 0 : nret = NSS_STATUS_SUCCESS;
622 :
623 : out:
624 2 : sss_nss_unlock();
625 2 : return nret;
626 : }
627 :
628 0 : enum nss_status _nss_sss_setgrent(void)
629 : {
630 : enum nss_status nret;
631 : int errnop;
632 :
633 0 : sss_nss_lock();
634 :
635 : /* make sure we do not have leftovers, and release memory */
636 0 : sss_nss_getgrent_data_clean();
637 :
638 0 : nret = sss_nss_make_request(SSS_NSS_SETGRENT,
639 : NULL, NULL, NULL, &errnop);
640 0 : if (nret != NSS_STATUS_SUCCESS) {
641 0 : errno = errnop;
642 : }
643 :
644 0 : sss_nss_unlock();
645 0 : return nret;
646 : }
647 :
648 0 : static enum nss_status internal_getgrent_r(struct group *result,
649 : char *buffer, size_t buflen,
650 : int *errnop)
651 : {
652 : struct sss_cli_req_data rd;
653 : struct sss_nss_gr_rep grrep;
654 : uint8_t *repbuf;
655 : size_t replen;
656 : uint32_t num_results;
657 : enum nss_status nret;
658 : uint32_t num_entries;
659 : int ret;
660 :
661 : /* Caught once glibc passing in buffer == 0x0 */
662 0 : if (!buffer || !buflen) return ERANGE;
663 :
664 : /* if there are leftovers return the next one */
665 0 : if (sss_nss_getgrent_data.data != NULL &&
666 0 : sss_nss_getgrent_data.ptr < sss_nss_getgrent_data.len) {
667 :
668 0 : repbuf = (uint8_t *)sss_nss_getgrent_data.data +
669 : sss_nss_getgrent_data.ptr;
670 0 : replen = sss_nss_getgrent_data.len -
671 0 : sss_nss_getgrent_data.ptr;
672 :
673 0 : grrep.result = result;
674 0 : grrep.buffer = buffer;
675 0 : grrep.buflen = buflen;
676 :
677 0 : ret = sss_nss_getgr_readrep(&grrep, repbuf, &replen);
678 0 : if (ret) {
679 0 : *errnop = ret;
680 0 : return NSS_STATUS_TRYAGAIN;
681 : }
682 :
683 : /* advance buffer pointer */
684 0 : sss_nss_getgrent_data.ptr = sss_nss_getgrent_data.len - replen;
685 :
686 0 : return NSS_STATUS_SUCCESS;
687 : }
688 :
689 : /* release memory if any */
690 0 : sss_nss_getgrent_data_clean();
691 :
692 : /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */
693 0 : num_entries = SSS_NSS_MAX_ENTRIES;
694 0 : rd.len = sizeof(uint32_t);
695 0 : rd.data = &num_entries;
696 :
697 0 : nret = sss_nss_make_request(SSS_NSS_GETGRENT, &rd,
698 : &repbuf, &replen, errnop);
699 0 : if (nret != NSS_STATUS_SUCCESS) {
700 0 : return nret;
701 : }
702 :
703 : /* Get number of results from repbuf. */
704 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
705 :
706 : /* no results if not found */
707 0 : if ((num_results == 0) || (replen - 8 == 0)) {
708 0 : free(repbuf);
709 0 : return NSS_STATUS_NOTFOUND;
710 : }
711 :
712 0 : sss_nss_getgrent_data.data = repbuf;
713 0 : sss_nss_getgrent_data.len = replen;
714 0 : sss_nss_getgrent_data.ptr = 8; /* skip metadata fields */
715 :
716 : /* call again ourselves, this will return the first result */
717 0 : return internal_getgrent_r(result, buffer, buflen, errnop);
718 : }
719 :
720 0 : enum nss_status _nss_sss_getgrent_r(struct group *result,
721 : char *buffer, size_t buflen, int *errnop)
722 : {
723 : enum nss_status nret;
724 :
725 0 : sss_nss_lock();
726 0 : nret = internal_getgrent_r(result, buffer, buflen, errnop);
727 0 : sss_nss_unlock();
728 :
729 0 : return nret;
730 : }
731 :
732 0 : enum nss_status _nss_sss_endgrent(void)
733 : {
734 : enum nss_status nret;
735 : int errnop;
736 :
737 0 : sss_nss_lock();
738 :
739 : /* make sure we do not have leftovers, and release memory */
740 0 : sss_nss_getgrent_data_clean();
741 :
742 0 : nret = sss_nss_make_request(SSS_NSS_ENDGRENT,
743 : NULL, NULL, NULL, &errnop);
744 0 : if (nret != NSS_STATUS_SUCCESS) {
745 0 : errno = errnop;
746 : }
747 :
748 0 : sss_nss_unlock();
749 0 : return nret;
750 : }
|