selftest: fix for wbinfo -s tests for wellknown SIDs
[gd/samba-autobuild/.git] / nsswitch / winbind_nss_linux.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Windows NT Domain nsswitch module
5
6    Copyright (C) Tim Potter 2000
7
8    This library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public
10    License as published by the Free Software Foundation; either
11    version 3 of the License, or (at your option) any later version.
12
13    This library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "winbind_client.h"
23
24 #if HAVE_PTHREAD_H
25 #include <pthread.h>
26 #endif
27
28 #if HAVE_PTHREAD
29 static pthread_mutex_t winbind_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
30 #endif
31
32 /* Maximum number of users to pass back over the unix domain socket
33    per call. This is not a static limit on the total number of users
34    or groups returned in total. */
35
36 #define MAX_GETPWENT_USERS 250
37 #define MAX_GETGRENT_USERS 250
38
39 NSS_STATUS _nss_winbind_setpwent(void);
40 NSS_STATUS _nss_winbind_endpwent(void);
41 NSS_STATUS _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
42                                    size_t buflen, int *errnop);
43 NSS_STATUS _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result,
44                                    char *buffer, size_t buflen, int *errnop);
45 NSS_STATUS _nss_winbind_getpwnam_r(const char *name, struct passwd *result,
46                                    char *buffer, size_t buflen, int *errnop);
47 NSS_STATUS _nss_winbind_setgrent(void);
48 NSS_STATUS _nss_winbind_endgrent(void);
49 NSS_STATUS _nss_winbind_getgrent_r(struct group *result, char *buffer,
50                                    size_t buflen, int *errnop);
51 NSS_STATUS _nss_winbind_getgrlst_r(struct group *result, char *buffer,
52                                    size_t buflen, int *errnop);
53 NSS_STATUS _nss_winbind_getgrnam_r(const char *name, struct group *result,
54                                    char *buffer, size_t buflen, int *errnop);
55 NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid, struct group *result, char *buffer,
56                                    size_t buflen, int *errnop);
57 NSS_STATUS _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
58                                        long int *size, gid_t **groups,
59                                        long int limit, int *errnop);
60
61 /*************************************************************************
62  ************************************************************************/
63
64 #ifdef DEBUG_NSS
65 static const char *nss_err_str(NSS_STATUS ret)
66 {
67         switch (ret) {
68                 case NSS_STATUS_TRYAGAIN:
69                         return "NSS_STATUS_TRYAGAIN";
70                 case NSS_STATUS_SUCCESS:
71                         return "NSS_STATUS_SUCCESS";
72                 case NSS_STATUS_NOTFOUND:
73                         return "NSS_STATUS_NOTFOUND";
74                 case NSS_STATUS_UNAVAIL:
75                         return "NSS_STATUS_UNAVAIL";
76 #ifdef NSS_STATUS_RETURN
77                 case NSS_STATUS_RETURN:
78                         return "NSS_STATUS_RETURN";
79 #endif
80                 default:
81                         return "UNKNOWN RETURN CODE!!!!!!!";
82         }
83 }
84 #endif
85
86 /* Prototypes from wb_common.c */
87
88 /* Allocate some space from the nss static buffer.  The buffer and buflen
89    are the pointers passed in by the C library to the _nss_ntdom_*
90    functions. */
91
92 static char *get_static(char **buffer, size_t *buflen, size_t len)
93 {
94         char *result;
95
96         /* Error check.  We return false if things aren't set up right, or
97            there isn't enough buffer space left. */
98
99         if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
100                 return NULL;
101         }
102
103         /* Return an index into the static buffer */
104
105         result = *buffer;
106         *buffer += len;
107         *buflen -= len;
108
109         return result;
110 }
111
112 /* I've copied the strtok() replacement function next_token_Xalloc() from
113    lib/util_str.c as I really don't want to have to link in any other
114    objects if I can possibly avoid it. */
115
116 static bool next_token_alloc(const char **ptr,
117                                 char **pp_buff,
118                                 const char *sep)
119 {
120         const char *s;
121         const char *saved_s;
122         char *pbuf;
123         bool quoted;
124         size_t len=1;
125
126         *pp_buff = NULL;
127         if (!ptr) {
128                 return(false);
129         }
130
131         s = *ptr;
132
133         /* default to simple separators */
134         if (!sep) {
135                 sep = " \t\n\r";
136         }
137
138         /* find the first non sep char */
139         while (*s && strchr(sep,*s)) {
140                 s++;
141         }
142
143         /* nothing left? */
144         if (!*s) {
145                 return false;
146         }
147
148         /* When restarting we need to go from here. */
149         saved_s = s;
150
151         /* Work out the length needed. */
152         for (quoted = false; *s &&
153                         (quoted || !strchr(sep,*s)); s++) {
154                 if (*s == '\"') {
155                         quoted = !quoted;
156                 } else {
157                         len++;
158                 }
159         }
160
161         /* We started with len = 1 so we have space for the nul. */
162         *pp_buff = (char *)malloc(len);
163         if (!*pp_buff) {
164                 return false;
165         }
166
167         /* copy over the token */
168         pbuf = *pp_buff;
169         s = saved_s;
170         for (quoted = false; *s &&
171                         (quoted || !strchr(sep,*s)); s++) {
172                 if ( *s == '\"' ) {
173                         quoted = !quoted;
174                 } else {
175                         *pbuf++ = *s;
176                 }
177         }
178
179         *ptr = (*s) ? s+1 : s;
180         *pbuf = 0;
181
182         return true;
183 }
184
185 /* Fill a pwent structure from a winbindd_response structure.  We use
186    the static data passed to us by libc to put strings and stuff in.
187    Return NSS_STATUS_TRYAGAIN if we run out of memory. */
188
189 static NSS_STATUS fill_pwent(struct passwd *result,
190                                   struct winbindd_pw *pw,
191                                   char **buffer, size_t *buflen)
192 {
193         size_t len;
194
195         /* User name */
196         len = strlen(pw->pw_name) + 1;
197
198         if ((result->pw_name =
199              get_static(buffer, buflen, len)) == NULL) {
200
201                 /* Out of memory */
202
203                 return NSS_STATUS_TRYAGAIN;
204         }
205
206         memcpy(result->pw_name, pw->pw_name, len);
207
208         /* Password */
209         len = strlen(pw->pw_passwd) + 1;
210
211         if ((result->pw_passwd =
212              get_static(buffer, buflen, len)) == NULL) {
213
214                 /* Out of memory */
215
216                 return NSS_STATUS_TRYAGAIN;
217         }
218
219         memcpy(result->pw_passwd, pw->pw_passwd, len);
220
221         /* [ug]id */
222
223         result->pw_uid = pw->pw_uid;
224         result->pw_gid = pw->pw_gid;
225
226         /* GECOS */
227         len = strlen(pw->pw_gecos) + 1;
228
229         if ((result->pw_gecos =
230              get_static(buffer, buflen, len)) == NULL) {
231
232                 /* Out of memory */
233
234                 return NSS_STATUS_TRYAGAIN;
235         }
236
237         memcpy(result->pw_gecos, pw->pw_gecos, len);
238
239         /* Home directory */
240         len = strlen(pw->pw_dir) + 1;
241
242         if ((result->pw_dir =
243              get_static(buffer, buflen, len)) == NULL) {
244
245                 /* Out of memory */
246
247                 return NSS_STATUS_TRYAGAIN;
248         }
249
250         memcpy(result->pw_dir, pw->pw_dir, len);
251
252         /* Logon shell */
253         len = strlen(pw->pw_shell) + 1;
254
255         if ((result->pw_shell =
256              get_static(buffer, buflen, len)) == NULL) {
257
258                 /* Out of memory */
259
260                 return NSS_STATUS_TRYAGAIN;
261         }
262
263         memcpy(result->pw_shell, pw->pw_shell, len);
264
265         /* The struct passwd for Solaris has some extra fields which must
266            be initialised or nscd crashes. */
267
268 #if HAVE_PASSWD_PW_COMMENT
269         result->pw_comment = "";
270 #endif
271
272 #if HAVE_PASSWD_PW_AGE
273         result->pw_age = "";
274 #endif
275
276         return NSS_STATUS_SUCCESS;
277 }
278
279 /* Fill a grent structure from a winbindd_response structure.  We use
280    the static data passed to us by libc to put strings and stuff in.
281    Return NSS_STATUS_TRYAGAIN if we run out of memory. */
282
283 static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
284                       const char *gr_mem, char **buffer, size_t *buflen)
285 {
286         char *name;
287         int i;
288         char *tst;
289         size_t len;
290
291         /* Group name */
292         len = strlen(gr->gr_name) + 1;
293
294         if ((result->gr_name =
295              get_static(buffer, buflen, len)) == NULL) {
296
297                 /* Out of memory */
298
299                 return NSS_STATUS_TRYAGAIN;
300         }
301
302         memcpy(result->gr_name, gr->gr_name, len);
303
304         /* Password */
305         len = strlen(gr->gr_passwd) + 1;
306
307         if ((result->gr_passwd =
308              get_static(buffer, buflen, len)) == NULL) {
309
310                 /* Out of memory */
311                 return NSS_STATUS_TRYAGAIN;
312         }
313
314         memcpy(result->gr_passwd, gr->gr_passwd, len);
315
316         /* gid */
317
318         result->gr_gid = gr->gr_gid;
319
320         /* Group membership */
321
322         if (!gr_mem) {
323                 gr->num_gr_mem = 0;
324         }
325
326         /* this next value is a pointer to a pointer so let's align it */
327
328         /* Calculate number of extra bytes needed to align on pointer size boundry */
329         if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
330                 i = sizeof(char*) - i;
331
332         if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
333                                  sizeof(char *)+i))) == NULL) {
334
335                 /* Out of memory */
336
337                 return NSS_STATUS_TRYAGAIN;
338         }
339         result->gr_mem = (char **)(tst + i);
340
341         if (gr->num_gr_mem == 0) {
342
343                 /* Group is empty */
344
345                 *(result->gr_mem) = NULL;
346                 return NSS_STATUS_SUCCESS;
347         }
348
349         /* Start looking at extra data */
350
351         i = 0;
352
353         while(next_token_alloc((const char **)&gr_mem, &name, ",")) {
354                 /* Allocate space for member */
355                 len = strlen(name) + 1;
356
357                 if (((result->gr_mem)[i] =
358                      get_static(buffer, buflen, len)) == NULL) {
359                         free(name);
360                         /* Out of memory */
361                         return NSS_STATUS_TRYAGAIN;
362                 }
363                 memcpy((result->gr_mem)[i], name, len);
364                 free(name);
365                 i++;
366         }
367
368         /* Terminate list */
369
370         (result->gr_mem)[i] = NULL;
371
372         return NSS_STATUS_SUCCESS;
373 }
374
375 /*
376  * NSS user functions
377  */
378
379 static struct winbindd_response getpwent_response;
380
381 static int ndx_pw_cache;                 /* Current index into pwd cache */
382 static int num_pw_cache;                 /* Current size of pwd cache */
383
384 /* Rewind "file pointer" to start of ntdom password database */
385
386 NSS_STATUS
387 _nss_winbind_setpwent(void)
388 {
389         NSS_STATUS ret;
390 #ifdef DEBUG_NSS
391         fprintf(stderr, "[%5d]: setpwent\n", getpid());
392 #endif
393
394 #if HAVE_PTHREAD
395         pthread_mutex_lock(&winbind_nss_mutex);
396 #endif
397
398         if (num_pw_cache > 0) {
399                 ndx_pw_cache = num_pw_cache = 0;
400                 winbindd_free_response(&getpwent_response);
401         }
402
403         ret = winbindd_request_response(NULL, WINBINDD_SETPWENT, NULL, NULL);
404 #ifdef DEBUG_NSS
405         fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(),
406                 nss_err_str(ret), ret);
407 #endif
408
409 #if HAVE_PTHREAD
410         pthread_mutex_unlock(&winbind_nss_mutex);
411 #endif
412         return ret;
413 }
414
415 /* Close ntdom password database "file pointer" */
416
417 NSS_STATUS
418 _nss_winbind_endpwent(void)
419 {
420         NSS_STATUS ret;
421 #ifdef DEBUG_NSS
422         fprintf(stderr, "[%5d]: endpwent\n", getpid());
423 #endif
424
425 #if HAVE_PTHREAD
426         pthread_mutex_lock(&winbind_nss_mutex);
427 #endif
428
429         if (num_pw_cache > 0) {
430                 ndx_pw_cache = num_pw_cache = 0;
431                 winbindd_free_response(&getpwent_response);
432         }
433
434         ret = winbindd_request_response(NULL, WINBINDD_ENDPWENT, NULL, NULL);
435 #ifdef DEBUG_NSS
436         fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(),
437                 nss_err_str(ret), ret);
438 #endif
439
440 #if HAVE_PTHREAD
441         pthread_mutex_unlock(&winbind_nss_mutex);
442 #endif
443
444         return ret;
445 }
446
447 /* Fetch the next password entry from ntdom password database */
448
449 NSS_STATUS
450 _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
451                         size_t buflen, int *errnop)
452 {
453         NSS_STATUS ret;
454         struct winbindd_request request;
455         static int called_again;
456
457 #ifdef DEBUG_NSS
458         fprintf(stderr, "[%5d]: getpwent\n", getpid());
459 #endif
460
461 #if HAVE_PTHREAD
462         pthread_mutex_lock(&winbind_nss_mutex);
463 #endif
464
465         /* Return an entry from the cache if we have one, or if we are
466            called again because we exceeded our static buffer.  */
467
468         if ((ndx_pw_cache < num_pw_cache) || called_again) {
469                 goto return_result;
470         }
471
472         /* Else call winbindd to get a bunch of entries */
473
474         if (num_pw_cache > 0) {
475                 winbindd_free_response(&getpwent_response);
476         }
477
478         ZERO_STRUCT(request);
479         ZERO_STRUCT(getpwent_response);
480
481         request.data.num_entries = MAX_GETPWENT_USERS;
482
483         ret = winbindd_request_response(NULL, WINBINDD_GETPWENT, &request,
484                                &getpwent_response);
485
486         if (ret == NSS_STATUS_SUCCESS) {
487                 struct winbindd_pw *pw_cache;
488
489                 /* Fill cache */
490
491                 ndx_pw_cache = 0;
492                 num_pw_cache = getpwent_response.data.num_entries;
493
494                 /* Return a result */
495
496         return_result:
497
498                 pw_cache = (struct winbindd_pw *)
499                         getpwent_response.extra_data.data;
500
501                 /* Check data is valid */
502
503                 if (pw_cache == NULL) {
504                         ret = NSS_STATUS_NOTFOUND;
505                         goto done;
506                 }
507
508                 ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
509                                  &buffer, &buflen);
510
511                 /* Out of memory - try again */
512
513                 if (ret == NSS_STATUS_TRYAGAIN) {
514                         called_again = true;
515                         *errnop = errno = ERANGE;
516                         goto done;
517                 }
518
519                 *errnop = errno = 0;
520                 called_again = false;
521                 ndx_pw_cache++;
522
523                 /* If we've finished with this lot of results free cache */
524
525                 if (ndx_pw_cache == num_pw_cache) {
526                         ndx_pw_cache = num_pw_cache = 0;
527                         winbindd_free_response(&getpwent_response);
528                 }
529         }
530         done:
531 #ifdef DEBUG_NSS
532         fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(),
533                 nss_err_str(ret), ret);
534 #endif
535
536 #if HAVE_PTHREAD
537         pthread_mutex_unlock(&winbind_nss_mutex);
538 #endif
539         return ret;
540 }
541
542 /* Return passwd struct from uid */
543
544 NSS_STATUS
545 _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
546                         size_t buflen, int *errnop)
547 {
548         NSS_STATUS ret;
549         static struct winbindd_response response;
550         struct winbindd_request request;
551         static int keep_response;
552
553 #ifdef DEBUG_NSS
554         fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid);
555 #endif
556
557 #if HAVE_PTHREAD
558         pthread_mutex_lock(&winbind_nss_mutex);
559 #endif
560
561         /* If our static buffer needs to be expanded we are called again */
562         if (!keep_response || uid != response.data.pw.pw_uid) {
563
564                 /* Call for the first time */
565
566                 ZERO_STRUCT(response);
567                 ZERO_STRUCT(request);
568
569                 request.data.uid = uid;
570
571                 ret = winbindd_request_response(NULL, WINBINDD_GETPWUID, &request, &response);
572
573                 if (ret == NSS_STATUS_SUCCESS) {
574                         ret = fill_pwent(result, &response.data.pw,
575                                          &buffer, &buflen);
576
577                         if (ret == NSS_STATUS_TRYAGAIN) {
578                                 keep_response = true;
579                                 *errnop = errno = ERANGE;
580                                 goto done;
581                         }
582                 }
583
584         } else {
585
586                 /* We've been called again */
587
588                 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
589
590                 if (ret == NSS_STATUS_TRYAGAIN) {
591                         *errnop = errno = ERANGE;
592                         goto done;
593                 }
594
595                 keep_response = false;
596                 *errnop = errno = 0;
597         }
598
599         winbindd_free_response(&response);
600
601         done:
602
603 #ifdef DEBUG_NSS
604         fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
605                 (unsigned int)uid, nss_err_str(ret), ret);
606 #endif
607
608 #if HAVE_PTHREAD
609         pthread_mutex_unlock(&winbind_nss_mutex);
610 #endif
611
612         return ret;
613 }
614
615 /* Return passwd struct from username */
616 NSS_STATUS
617 _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
618                         size_t buflen, int *errnop)
619 {
620         NSS_STATUS ret;
621         static struct winbindd_response response;
622         struct winbindd_request request;
623         static int keep_response;
624
625 #ifdef DEBUG_NSS
626         fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name);
627 #endif
628
629 #if HAVE_PTHREAD
630         pthread_mutex_lock(&winbind_nss_mutex);
631 #endif
632
633         /* If our static buffer needs to be expanded we are called again */
634
635         if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) {
636
637                 /* Call for the first time */
638
639                 ZERO_STRUCT(response);
640                 ZERO_STRUCT(request);
641
642                 strncpy(request.data.username, name,
643                         sizeof(request.data.username) - 1);
644                 request.data.username
645                         [sizeof(request.data.username) - 1] = '\0';
646
647                 ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM, &request, &response);
648
649                 if (ret == NSS_STATUS_SUCCESS) {
650                         ret = fill_pwent(result, &response.data.pw, &buffer,
651                                          &buflen);
652
653                         if (ret == NSS_STATUS_TRYAGAIN) {
654                                 keep_response = true;
655                                 *errnop = errno = ERANGE;
656                                 goto done;
657                         }
658                 }
659
660         } else {
661
662                 /* We've been called again */
663
664                 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
665
666                 if (ret == NSS_STATUS_TRYAGAIN) {
667                         keep_response = true;
668                         *errnop = errno = ERANGE;
669                         goto done;
670                 }
671
672                 keep_response = false;
673                 *errnop = errno = 0;
674         }
675
676         winbindd_free_response(&response);
677         done:
678 #ifdef DEBUG_NSS
679         fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
680                 name, nss_err_str(ret), ret);
681 #endif
682
683 #if HAVE_PTHREAD
684         pthread_mutex_unlock(&winbind_nss_mutex);
685 #endif
686
687         return ret;
688 }
689
690 /*
691  * NSS group functions
692  */
693
694 static struct winbindd_response getgrent_response;
695
696 static int ndx_gr_cache;                 /* Current index into grp cache */
697 static int num_gr_cache;                 /* Current size of grp cache */
698
699 /* Rewind "file pointer" to start of ntdom group database */
700
701 NSS_STATUS
702 _nss_winbind_setgrent(void)
703 {
704         NSS_STATUS ret;
705 #ifdef DEBUG_NSS
706         fprintf(stderr, "[%5d]: setgrent\n", getpid());
707 #endif
708
709 #if HAVE_PTHREAD
710         pthread_mutex_lock(&winbind_nss_mutex);
711 #endif
712
713         if (num_gr_cache > 0) {
714                 ndx_gr_cache = num_gr_cache = 0;
715                 winbindd_free_response(&getgrent_response);
716         }
717
718         ret = winbindd_request_response(NULL, WINBINDD_SETGRENT, NULL, NULL);
719 #ifdef DEBUG_NSS
720         fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(),
721                 nss_err_str(ret), ret);
722 #endif
723
724 #if HAVE_PTHREAD
725         pthread_mutex_unlock(&winbind_nss_mutex);
726 #endif
727
728         return ret;
729 }
730
731 /* Close "file pointer" for ntdom group database */
732
733 NSS_STATUS
734 _nss_winbind_endgrent(void)
735 {
736         NSS_STATUS ret;
737 #ifdef DEBUG_NSS
738         fprintf(stderr, "[%5d]: endgrent\n", getpid());
739 #endif
740
741 #if HAVE_PTHREAD
742         pthread_mutex_lock(&winbind_nss_mutex);
743 #endif
744
745         if (num_gr_cache > 0) {
746                 ndx_gr_cache = num_gr_cache = 0;
747                 winbindd_free_response(&getgrent_response);
748         }
749
750         ret = winbindd_request_response(NULL, WINBINDD_ENDGRENT, NULL, NULL);
751 #ifdef DEBUG_NSS
752         fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(),
753                 nss_err_str(ret), ret);
754 #endif
755
756 #if HAVE_PTHREAD
757         pthread_mutex_unlock(&winbind_nss_mutex);
758 #endif
759
760         return ret;
761 }
762
763 /* Get next entry from ntdom group database */
764
765 static NSS_STATUS
766 winbind_getgrent(enum winbindd_cmd cmd,
767                  struct group *result,
768                  char *buffer, size_t buflen, int *errnop)
769 {
770         NSS_STATUS ret;
771         static struct winbindd_request request;
772         static int called_again;
773
774
775 #ifdef DEBUG_NSS
776         fprintf(stderr, "[%5d]: getgrent\n", getpid());
777 #endif
778
779 #if HAVE_PTHREAD
780         pthread_mutex_lock(&winbind_nss_mutex);
781 #endif
782
783         /* Return an entry from the cache if we have one, or if we are
784            called again because we exceeded our static buffer.  */
785
786         if ((ndx_gr_cache < num_gr_cache) || called_again) {
787                 goto return_result;
788         }
789
790         /* Else call winbindd to get a bunch of entries */
791
792         if (num_gr_cache > 0) {
793                 winbindd_free_response(&getgrent_response);
794         }
795
796         ZERO_STRUCT(request);
797         ZERO_STRUCT(getgrent_response);
798
799         request.data.num_entries = MAX_GETGRENT_USERS;
800
801         ret = winbindd_request_response(NULL, cmd, &request,
802                                &getgrent_response);
803
804         if (ret == NSS_STATUS_SUCCESS) {
805                 struct winbindd_gr *gr_cache;
806                 int mem_ofs;
807
808                 /* Fill cache */
809
810                 ndx_gr_cache = 0;
811                 num_gr_cache = getgrent_response.data.num_entries;
812
813                 /* Return a result */
814
815         return_result:
816
817                 gr_cache = (struct winbindd_gr *)
818                         getgrent_response.extra_data.data;
819
820                 /* Check data is valid */
821
822                 if (gr_cache == NULL) {
823                         ret = NSS_STATUS_NOTFOUND;
824                         goto done;
825                 }
826
827                 /* Fill group membership.  The offset into the extra data
828                    for the group membership is the reported offset plus the
829                    size of all the winbindd_gr records returned. */
830
831                 mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
832                         num_gr_cache * sizeof(struct winbindd_gr);
833
834                 ret = fill_grent(result, &gr_cache[ndx_gr_cache],
835                                  ((char *)getgrent_response.extra_data.data)+mem_ofs,
836                                  &buffer, &buflen);
837
838                 /* Out of memory - try again */
839
840                 if (ret == NSS_STATUS_TRYAGAIN) {
841                         called_again = true;
842                         *errnop = errno = ERANGE;
843                         goto done;
844                 }
845
846                 *errnop = 0;
847                 called_again = false;
848                 ndx_gr_cache++;
849
850                 /* If we've finished with this lot of results free cache */
851
852                 if (ndx_gr_cache == num_gr_cache) {
853                         ndx_gr_cache = num_gr_cache = 0;
854                         winbindd_free_response(&getgrent_response);
855                 }
856         }
857         done:
858 #ifdef DEBUG_NSS
859         fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(),
860                 nss_err_str(ret), ret);
861 #endif
862
863 #if HAVE_PTHREAD
864         pthread_mutex_unlock(&winbind_nss_mutex);
865 #endif
866
867         return ret;
868 }
869
870
871 NSS_STATUS
872 _nss_winbind_getgrent_r(struct group *result,
873                         char *buffer, size_t buflen, int *errnop)
874 {
875         return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
876 }
877
878 NSS_STATUS
879 _nss_winbind_getgrlst_r(struct group *result,
880                         char *buffer, size_t buflen, int *errnop)
881 {
882         return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
883 }
884
885 /* Return group struct from group name */
886
887 NSS_STATUS
888 _nss_winbind_getgrnam_r(const char *name,
889                         struct group *result, char *buffer,
890                         size_t buflen, int *errnop)
891 {
892         NSS_STATUS ret;
893         static struct winbindd_response response;
894         struct winbindd_request request;
895         static int keep_response;
896
897 #ifdef DEBUG_NSS
898         fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
899 #endif
900
901 #if HAVE_PTHREAD
902         pthread_mutex_lock(&winbind_nss_mutex);
903 #endif
904
905         /* If our static buffer needs to be expanded we are called again */
906         /* Or if the stored response group name differs from the request. */
907
908         if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) {
909
910                 /* Call for the first time */
911
912                 ZERO_STRUCT(request);
913                 ZERO_STRUCT(response);
914
915                 strncpy(request.data.groupname, name,
916                         sizeof(request.data.groupname));
917                 request.data.groupname
918                         [sizeof(request.data.groupname) - 1] = '\0';
919
920                 ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
921                                                 &request, &response);
922
923                 if (ret == NSS_STATUS_SUCCESS) {
924                         ret = fill_grent(result, &response.data.gr,
925                                          (char *)response.extra_data.data,
926                                          &buffer, &buflen);
927
928                         if (ret == NSS_STATUS_TRYAGAIN) {
929                                 keep_response = true;
930                                 *errnop = errno = ERANGE;
931                                 goto done;
932                         }
933                 }
934
935         } else {
936
937                 /* We've been called again */
938
939                 ret = fill_grent(result, &response.data.gr,
940                                  (char *)response.extra_data.data, &buffer,
941                                  &buflen);
942
943                 if (ret == NSS_STATUS_TRYAGAIN) {
944                         keep_response = true;
945                         *errnop = errno = ERANGE;
946                         goto done;
947                 }
948
949                 keep_response = false;
950                 *errnop = 0;
951         }
952
953         winbindd_free_response(&response);
954         done:
955 #ifdef DEBUG_NSS
956         fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
957                 name, nss_err_str(ret), ret);
958 #endif
959
960 #if HAVE_PTHREAD
961         pthread_mutex_unlock(&winbind_nss_mutex);
962 #endif
963
964         return ret;
965 }
966
967 /* Return group struct from gid */
968
969 NSS_STATUS
970 _nss_winbind_getgrgid_r(gid_t gid,
971                         struct group *result, char *buffer,
972                         size_t buflen, int *errnop)
973 {
974         NSS_STATUS ret;
975         static struct winbindd_response response;
976         struct winbindd_request request;
977         static int keep_response;
978
979 #ifdef DEBUG_NSS
980         fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
981 #endif
982
983 #if HAVE_PTHREAD
984         pthread_mutex_lock(&winbind_nss_mutex);
985 #endif
986
987         /* If our static buffer needs to be expanded we are called again */
988         /* Or if the stored response group name differs from the request. */
989
990         if (!keep_response || gid != response.data.gr.gr_gid) {
991
992                 /* Call for the first time */
993
994                 ZERO_STRUCT(request);
995                 ZERO_STRUCT(response);
996
997                 request.data.gid = gid;
998
999                 ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
1000                                                 &request, &response);
1001
1002                 if (ret == NSS_STATUS_SUCCESS) {
1003
1004                         ret = fill_grent(result, &response.data.gr,
1005                                          (char *)response.extra_data.data,
1006                                          &buffer, &buflen);
1007
1008                         if (ret == NSS_STATUS_TRYAGAIN) {
1009                                 keep_response = true;
1010                                 *errnop = errno = ERANGE;
1011                                 goto done;
1012                         }
1013                 }
1014
1015         } else {
1016
1017                 /* We've been called again */
1018
1019                 ret = fill_grent(result, &response.data.gr,
1020                                  (char *)response.extra_data.data, &buffer,
1021                                  &buflen);
1022
1023                 if (ret == NSS_STATUS_TRYAGAIN) {
1024                         keep_response = true;
1025                         *errnop = errno = ERANGE;
1026                         goto done;
1027                 }
1028
1029                 keep_response = false;
1030                 *errnop = 0;
1031         }
1032
1033         winbindd_free_response(&response);
1034         done:
1035 #ifdef DEBUG_NSS
1036         fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
1037                 (unsigned int)gid, nss_err_str(ret), ret);
1038 #endif
1039
1040 #if HAVE_PTHREAD
1041         pthread_mutex_unlock(&winbind_nss_mutex);
1042 #endif
1043         return ret;
1044 }
1045
1046 /* Initialise supplementary groups */
1047
1048 NSS_STATUS
1049 _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
1050                             long int *size, gid_t **groups, long int limit,
1051                             int *errnop)
1052 {
1053         NSS_STATUS ret;
1054         struct winbindd_request request;
1055         struct winbindd_response response;
1056         int i;
1057
1058 #ifdef DEBUG_NSS
1059         fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
1060                 user, group);
1061 #endif
1062
1063 #if HAVE_PTHREAD
1064         pthread_mutex_lock(&winbind_nss_mutex);
1065 #endif
1066
1067         ZERO_STRUCT(request);
1068         ZERO_STRUCT(response);
1069
1070         strncpy(request.data.username, user,
1071                 sizeof(request.data.username) - 1);
1072
1073         ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
1074                                         &request, &response);
1075
1076         if (ret == NSS_STATUS_SUCCESS) {
1077                 int num_gids = response.data.num_entries;
1078                 gid_t *gid_list = (gid_t *)response.extra_data.data;
1079
1080 #ifdef DEBUG_NSS
1081                 fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
1082                                 "and %d gids\n", getpid(),
1083                                 user, num_gids);
1084 #endif
1085                 if (gid_list == NULL) {
1086                         ret = NSS_STATUS_NOTFOUND;
1087                         goto done;
1088                 }
1089
1090                 /* Copy group list to client */
1091
1092                 for (i = 0; i < num_gids; i++) {
1093
1094 #ifdef DEBUG_NSS
1095                         fprintf(stderr, "[%5d]: initgroups %s (%d): "
1096                                         "processing gid %d \n", getpid(),
1097                                         user, group, gid_list[i]);
1098 #endif
1099
1100                         /* Skip primary group */
1101
1102                         if (gid_list[i] == group) {
1103                                 continue;
1104                         }
1105
1106                         /* Skip groups without a mapping */
1107                         if (gid_list[i] == (uid_t)-1) {
1108                                 continue;
1109                         }
1110
1111                         /* Filled buffer ? If so, resize. */
1112
1113                         if (*start == *size) {
1114                                 long int newsize;
1115                                 gid_t *newgroups;
1116
1117                                 newsize = 2 * (*size);
1118                                 if (limit > 0) {
1119                                         if (*size == limit) {
1120                                                 goto done;
1121                                         }
1122                                         if (newsize > limit) {
1123                                                 newsize = limit;
1124                                         }
1125                                 }
1126
1127                                 newgroups = (gid_t *)
1128                                         realloc((*groups),
1129                                                 newsize * sizeof(**groups));
1130                                 if (!newgroups) {
1131                                         *errnop = ENOMEM;
1132                                         ret = NSS_STATUS_NOTFOUND;
1133                                         goto done;
1134                                 }
1135                                 *groups = newgroups;
1136                                 *size = newsize;
1137                         }
1138
1139                         /* Add to buffer */
1140
1141                         (*groups)[*start] = gid_list[i];
1142                         *start += 1;
1143                 }
1144         }
1145
1146         /* Back to your regularly scheduled programming */
1147
1148  done:
1149 #ifdef DEBUG_NSS
1150         fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1151                 user, nss_err_str(ret), ret);
1152 #endif
1153
1154 #if HAVE_PTHREAD
1155         pthread_mutex_unlock(&winbind_nss_mutex);
1156 #endif
1157
1158         return ret;
1159 }