winbind: Remove unused WINBINDD_SID_TO_UID
[samba.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         /* User name */
194
195         if ((result->pw_name =
196              get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
197
198                 /* Out of memory */
199
200                 return NSS_STATUS_TRYAGAIN;
201         }
202
203         strcpy(result->pw_name, pw->pw_name);
204
205         /* Password */
206
207         if ((result->pw_passwd =
208              get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
209
210                 /* Out of memory */
211
212                 return NSS_STATUS_TRYAGAIN;
213         }
214
215         strcpy(result->pw_passwd, pw->pw_passwd);
216
217         /* [ug]id */
218
219         result->pw_uid = pw->pw_uid;
220         result->pw_gid = pw->pw_gid;
221
222         /* GECOS */
223
224         if ((result->pw_gecos =
225              get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
226
227                 /* Out of memory */
228
229                 return NSS_STATUS_TRYAGAIN;
230         }
231
232         strcpy(result->pw_gecos, pw->pw_gecos);
233
234         /* Home directory */
235
236         if ((result->pw_dir =
237              get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
238
239                 /* Out of memory */
240
241                 return NSS_STATUS_TRYAGAIN;
242         }
243
244         strcpy(result->pw_dir, pw->pw_dir);
245
246         /* Logon shell */
247
248         if ((result->pw_shell =
249              get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
250
251                 /* Out of memory */
252
253                 return NSS_STATUS_TRYAGAIN;
254         }
255
256         strcpy(result->pw_shell, pw->pw_shell);
257
258         /* The struct passwd for Solaris has some extra fields which must
259            be initialised or nscd crashes. */
260
261 #if HAVE_PASSWD_PW_COMMENT
262         result->pw_comment = "";
263 #endif
264
265 #if HAVE_PASSWD_PW_AGE
266         result->pw_age = "";
267 #endif
268
269         return NSS_STATUS_SUCCESS;
270 }
271
272 /* Fill a grent structure from a winbindd_response structure.  We use
273    the static data passed to us by libc to put strings and stuff in.
274    Return NSS_STATUS_TRYAGAIN if we run out of memory. */
275
276 static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
277                       const char *gr_mem, char **buffer, size_t *buflen)
278 {
279         char *name;
280         int i;
281         char *tst;
282
283         /* Group name */
284
285         if ((result->gr_name =
286              get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
287
288                 /* Out of memory */
289
290                 return NSS_STATUS_TRYAGAIN;
291         }
292
293         strcpy(result->gr_name, gr->gr_name);
294
295         /* Password */
296
297         if ((result->gr_passwd =
298              get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
299
300                 /* Out of memory */
301                 return NSS_STATUS_TRYAGAIN;
302         }
303
304         strcpy(result->gr_passwd, gr->gr_passwd);
305
306         /* gid */
307
308         result->gr_gid = gr->gr_gid;
309
310         /* Group membership */
311
312         if (!gr_mem) {
313                 gr->num_gr_mem = 0;
314         }
315
316         /* this next value is a pointer to a pointer so let's align it */
317
318         /* Calculate number of extra bytes needed to align on pointer size boundry */
319         if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
320                 i = sizeof(char*) - i;
321
322         if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
323                                  sizeof(char *)+i))) == NULL) {
324
325                 /* Out of memory */
326
327                 return NSS_STATUS_TRYAGAIN;
328         }
329         result->gr_mem = (char **)(tst + i);
330
331         if (gr->num_gr_mem == 0) {
332
333                 /* Group is empty */
334
335                 *(result->gr_mem) = NULL;
336                 return NSS_STATUS_SUCCESS;
337         }
338
339         /* Start looking at extra data */
340
341         i = 0;
342
343         while(next_token_alloc((const char **)&gr_mem, &name, ",")) {
344                 /* Allocate space for member */
345                 if (((result->gr_mem)[i] =
346                      get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
347                         free(name);
348                         /* Out of memory */
349                         return NSS_STATUS_TRYAGAIN;
350                 }
351                 strcpy((result->gr_mem)[i], name);
352                 free(name);
353                 i++;
354         }
355
356         /* Terminate list */
357
358         (result->gr_mem)[i] = NULL;
359
360         return NSS_STATUS_SUCCESS;
361 }
362
363 /*
364  * NSS user functions
365  */
366
367 static struct winbindd_response getpwent_response;
368
369 static int ndx_pw_cache;                 /* Current index into pwd cache */
370 static int num_pw_cache;                 /* Current size of pwd cache */
371
372 /* Rewind "file pointer" to start of ntdom password database */
373
374 NSS_STATUS
375 _nss_winbind_setpwent(void)
376 {
377         NSS_STATUS ret;
378 #ifdef DEBUG_NSS
379         fprintf(stderr, "[%5d]: setpwent\n", getpid());
380 #endif
381
382 #if HAVE_PTHREAD
383         pthread_mutex_lock(&winbind_nss_mutex);
384 #endif
385
386         if (num_pw_cache > 0) {
387                 ndx_pw_cache = num_pw_cache = 0;
388                 winbindd_free_response(&getpwent_response);
389         }
390
391         ret = winbindd_request_response(NULL, WINBINDD_SETPWENT, NULL, NULL);
392 #ifdef DEBUG_NSS
393         fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(),
394                 nss_err_str(ret), ret);
395 #endif
396
397 #if HAVE_PTHREAD
398         pthread_mutex_unlock(&winbind_nss_mutex);
399 #endif
400         return ret;
401 }
402
403 /* Close ntdom password database "file pointer" */
404
405 NSS_STATUS
406 _nss_winbind_endpwent(void)
407 {
408         NSS_STATUS ret;
409 #ifdef DEBUG_NSS
410         fprintf(stderr, "[%5d]: endpwent\n", getpid());
411 #endif
412
413 #if HAVE_PTHREAD
414         pthread_mutex_lock(&winbind_nss_mutex);
415 #endif
416
417         if (num_pw_cache > 0) {
418                 ndx_pw_cache = num_pw_cache = 0;
419                 winbindd_free_response(&getpwent_response);
420         }
421
422         ret = winbindd_request_response(NULL, WINBINDD_ENDPWENT, NULL, NULL);
423 #ifdef DEBUG_NSS
424         fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(),
425                 nss_err_str(ret), ret);
426 #endif
427
428 #if HAVE_PTHREAD
429         pthread_mutex_unlock(&winbind_nss_mutex);
430 #endif
431
432         return ret;
433 }
434
435 /* Fetch the next password entry from ntdom password database */
436
437 NSS_STATUS
438 _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
439                         size_t buflen, int *errnop)
440 {
441         NSS_STATUS ret;
442         struct winbindd_request request;
443         static int called_again;
444
445 #ifdef DEBUG_NSS
446         fprintf(stderr, "[%5d]: getpwent\n", getpid());
447 #endif
448
449 #if HAVE_PTHREAD
450         pthread_mutex_lock(&winbind_nss_mutex);
451 #endif
452
453         /* Return an entry from the cache if we have one, or if we are
454            called again because we exceeded our static buffer.  */
455
456         if ((ndx_pw_cache < num_pw_cache) || called_again) {
457                 goto return_result;
458         }
459
460         /* Else call winbindd to get a bunch of entries */
461
462         if (num_pw_cache > 0) {
463                 winbindd_free_response(&getpwent_response);
464         }
465
466         ZERO_STRUCT(request);
467         ZERO_STRUCT(getpwent_response);
468
469         request.data.num_entries = MAX_GETPWENT_USERS;
470
471         ret = winbindd_request_response(NULL, WINBINDD_GETPWENT, &request,
472                                &getpwent_response);
473
474         if (ret == NSS_STATUS_SUCCESS) {
475                 struct winbindd_pw *pw_cache;
476
477                 /* Fill cache */
478
479                 ndx_pw_cache = 0;
480                 num_pw_cache = getpwent_response.data.num_entries;
481
482                 /* Return a result */
483
484         return_result:
485
486                 pw_cache = (struct winbindd_pw *)
487                         getpwent_response.extra_data.data;
488
489                 /* Check data is valid */
490
491                 if (pw_cache == NULL) {
492                         ret = NSS_STATUS_NOTFOUND;
493                         goto done;
494                 }
495
496                 ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
497                                  &buffer, &buflen);
498
499                 /* Out of memory - try again */
500
501                 if (ret == NSS_STATUS_TRYAGAIN) {
502                         called_again = true;
503                         *errnop = errno = ERANGE;
504                         goto done;
505                 }
506
507                 *errnop = errno = 0;
508                 called_again = false;
509                 ndx_pw_cache++;
510
511                 /* If we've finished with this lot of results free cache */
512
513                 if (ndx_pw_cache == num_pw_cache) {
514                         ndx_pw_cache = num_pw_cache = 0;
515                         winbindd_free_response(&getpwent_response);
516                 }
517         }
518         done:
519 #ifdef DEBUG_NSS
520         fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(),
521                 nss_err_str(ret), ret);
522 #endif
523
524 #if HAVE_PTHREAD
525         pthread_mutex_unlock(&winbind_nss_mutex);
526 #endif
527         return ret;
528 }
529
530 /* Return passwd struct from uid */
531
532 NSS_STATUS
533 _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
534                         size_t buflen, int *errnop)
535 {
536         NSS_STATUS ret;
537         static struct winbindd_response response;
538         struct winbindd_request request;
539         static int keep_response;
540
541 #ifdef DEBUG_NSS
542         fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid);
543 #endif
544
545 #if HAVE_PTHREAD
546         pthread_mutex_lock(&winbind_nss_mutex);
547 #endif
548
549         /* If our static buffer needs to be expanded we are called again */
550         if (!keep_response || uid != response.data.pw.pw_uid) {
551
552                 /* Call for the first time */
553
554                 ZERO_STRUCT(response);
555                 ZERO_STRUCT(request);
556
557                 request.data.uid = uid;
558
559                 ret = winbindd_request_response(NULL, WINBINDD_GETPWUID, &request, &response);
560
561                 if (ret == NSS_STATUS_SUCCESS) {
562                         ret = fill_pwent(result, &response.data.pw,
563                                          &buffer, &buflen);
564
565                         if (ret == NSS_STATUS_TRYAGAIN) {
566                                 keep_response = true;
567                                 *errnop = errno = ERANGE;
568                                 goto done;
569                         }
570                 }
571
572         } else {
573
574                 /* We've been called again */
575
576                 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
577
578                 if (ret == NSS_STATUS_TRYAGAIN) {
579                         *errnop = errno = ERANGE;
580                         goto done;
581                 }
582
583                 keep_response = false;
584                 *errnop = errno = 0;
585         }
586
587         winbindd_free_response(&response);
588
589         done:
590
591 #ifdef DEBUG_NSS
592         fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
593                 (unsigned int)uid, nss_err_str(ret), ret);
594 #endif
595
596 #if HAVE_PTHREAD
597         pthread_mutex_unlock(&winbind_nss_mutex);
598 #endif
599
600         return ret;
601 }
602
603 /* Return passwd struct from username */
604 NSS_STATUS
605 _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
606                         size_t buflen, int *errnop)
607 {
608         NSS_STATUS ret;
609         static struct winbindd_response response;
610         struct winbindd_request request;
611         static int keep_response;
612
613 #ifdef DEBUG_NSS
614         fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name);
615 #endif
616
617 #if HAVE_PTHREAD
618         pthread_mutex_lock(&winbind_nss_mutex);
619 #endif
620
621         /* If our static buffer needs to be expanded we are called again */
622
623         if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) {
624
625                 /* Call for the first time */
626
627                 ZERO_STRUCT(response);
628                 ZERO_STRUCT(request);
629
630                 strncpy(request.data.username, name,
631                         sizeof(request.data.username) - 1);
632                 request.data.username
633                         [sizeof(request.data.username) - 1] = '\0';
634
635                 ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM, &request, &response);
636
637                 if (ret == NSS_STATUS_SUCCESS) {
638                         ret = fill_pwent(result, &response.data.pw, &buffer,
639                                          &buflen);
640
641                         if (ret == NSS_STATUS_TRYAGAIN) {
642                                 keep_response = true;
643                                 *errnop = errno = ERANGE;
644                                 goto done;
645                         }
646                 }
647
648         } else {
649
650                 /* We've been called again */
651
652                 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
653
654                 if (ret == NSS_STATUS_TRYAGAIN) {
655                         keep_response = true;
656                         *errnop = errno = ERANGE;
657                         goto done;
658                 }
659
660                 keep_response = false;
661                 *errnop = errno = 0;
662         }
663
664         winbindd_free_response(&response);
665         done:
666 #ifdef DEBUG_NSS
667         fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
668                 name, nss_err_str(ret), ret);
669 #endif
670
671 #if HAVE_PTHREAD
672         pthread_mutex_unlock(&winbind_nss_mutex);
673 #endif
674
675         return ret;
676 }
677
678 /*
679  * NSS group functions
680  */
681
682 static struct winbindd_response getgrent_response;
683
684 static int ndx_gr_cache;                 /* Current index into grp cache */
685 static int num_gr_cache;                 /* Current size of grp cache */
686
687 /* Rewind "file pointer" to start of ntdom group database */
688
689 NSS_STATUS
690 _nss_winbind_setgrent(void)
691 {
692         NSS_STATUS ret;
693 #ifdef DEBUG_NSS
694         fprintf(stderr, "[%5d]: setgrent\n", getpid());
695 #endif
696
697 #if HAVE_PTHREAD
698         pthread_mutex_lock(&winbind_nss_mutex);
699 #endif
700
701         if (num_gr_cache > 0) {
702                 ndx_gr_cache = num_gr_cache = 0;
703                 winbindd_free_response(&getgrent_response);
704         }
705
706         ret = winbindd_request_response(NULL, WINBINDD_SETGRENT, NULL, NULL);
707 #ifdef DEBUG_NSS
708         fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(),
709                 nss_err_str(ret), ret);
710 #endif
711
712 #if HAVE_PTHREAD
713         pthread_mutex_unlock(&winbind_nss_mutex);
714 #endif
715
716         return ret;
717 }
718
719 /* Close "file pointer" for ntdom group database */
720
721 NSS_STATUS
722 _nss_winbind_endgrent(void)
723 {
724         NSS_STATUS ret;
725 #ifdef DEBUG_NSS
726         fprintf(stderr, "[%5d]: endgrent\n", getpid());
727 #endif
728
729 #if HAVE_PTHREAD
730         pthread_mutex_lock(&winbind_nss_mutex);
731 #endif
732
733         if (num_gr_cache > 0) {
734                 ndx_gr_cache = num_gr_cache = 0;
735                 winbindd_free_response(&getgrent_response);
736         }
737
738         ret = winbindd_request_response(NULL, WINBINDD_ENDGRENT, NULL, NULL);
739 #ifdef DEBUG_NSS
740         fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(),
741                 nss_err_str(ret), ret);
742 #endif
743
744 #if HAVE_PTHREAD
745         pthread_mutex_unlock(&winbind_nss_mutex);
746 #endif
747
748         return ret;
749 }
750
751 /* Get next entry from ntdom group database */
752
753 static NSS_STATUS
754 winbind_getgrent(enum winbindd_cmd cmd,
755                  struct group *result,
756                  char *buffer, size_t buflen, int *errnop)
757 {
758         NSS_STATUS ret;
759         static struct winbindd_request request;
760         static int called_again;
761
762
763 #ifdef DEBUG_NSS
764         fprintf(stderr, "[%5d]: getgrent\n", getpid());
765 #endif
766
767 #if HAVE_PTHREAD
768         pthread_mutex_lock(&winbind_nss_mutex);
769 #endif
770
771         /* Return an entry from the cache if we have one, or if we are
772            called again because we exceeded our static buffer.  */
773
774         if ((ndx_gr_cache < num_gr_cache) || called_again) {
775                 goto return_result;
776         }
777
778         /* Else call winbindd to get a bunch of entries */
779
780         if (num_gr_cache > 0) {
781                 winbindd_free_response(&getgrent_response);
782         }
783
784         ZERO_STRUCT(request);
785         ZERO_STRUCT(getgrent_response);
786
787         request.data.num_entries = MAX_GETGRENT_USERS;
788
789         ret = winbindd_request_response(NULL, cmd, &request,
790                                &getgrent_response);
791
792         if (ret == NSS_STATUS_SUCCESS) {
793                 struct winbindd_gr *gr_cache;
794                 int mem_ofs;
795
796                 /* Fill cache */
797
798                 ndx_gr_cache = 0;
799                 num_gr_cache = getgrent_response.data.num_entries;
800
801                 /* Return a result */
802
803         return_result:
804
805                 gr_cache = (struct winbindd_gr *)
806                         getgrent_response.extra_data.data;
807
808                 /* Check data is valid */
809
810                 if (gr_cache == NULL) {
811                         ret = NSS_STATUS_NOTFOUND;
812                         goto done;
813                 }
814
815                 /* Fill group membership.  The offset into the extra data
816                    for the group membership is the reported offset plus the
817                    size of all the winbindd_gr records returned. */
818
819                 mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
820                         num_gr_cache * sizeof(struct winbindd_gr);
821
822                 ret = fill_grent(result, &gr_cache[ndx_gr_cache],
823                                  ((char *)getgrent_response.extra_data.data)+mem_ofs,
824                                  &buffer, &buflen);
825
826                 /* Out of memory - try again */
827
828                 if (ret == NSS_STATUS_TRYAGAIN) {
829                         called_again = true;
830                         *errnop = errno = ERANGE;
831                         goto done;
832                 }
833
834                 *errnop = 0;
835                 called_again = false;
836                 ndx_gr_cache++;
837
838                 /* If we've finished with this lot of results free cache */
839
840                 if (ndx_gr_cache == num_gr_cache) {
841                         ndx_gr_cache = num_gr_cache = 0;
842                         winbindd_free_response(&getgrent_response);
843                 }
844         }
845         done:
846 #ifdef DEBUG_NSS
847         fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(),
848                 nss_err_str(ret), ret);
849 #endif
850
851 #if HAVE_PTHREAD
852         pthread_mutex_unlock(&winbind_nss_mutex);
853 #endif
854
855         return ret;
856 }
857
858
859 NSS_STATUS
860 _nss_winbind_getgrent_r(struct group *result,
861                         char *buffer, size_t buflen, int *errnop)
862 {
863         return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
864 }
865
866 NSS_STATUS
867 _nss_winbind_getgrlst_r(struct group *result,
868                         char *buffer, size_t buflen, int *errnop)
869 {
870         return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
871 }
872
873 /* Return group struct from group name */
874
875 NSS_STATUS
876 _nss_winbind_getgrnam_r(const char *name,
877                         struct group *result, char *buffer,
878                         size_t buflen, int *errnop)
879 {
880         NSS_STATUS ret;
881         static struct winbindd_response response;
882         struct winbindd_request request;
883         static int keep_response;
884
885 #ifdef DEBUG_NSS
886         fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
887 #endif
888
889 #if HAVE_PTHREAD
890         pthread_mutex_lock(&winbind_nss_mutex);
891 #endif
892
893         /* If our static buffer needs to be expanded we are called again */
894         /* Or if the stored response group name differs from the request. */
895
896         if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) {
897
898                 /* Call for the first time */
899
900                 ZERO_STRUCT(request);
901                 ZERO_STRUCT(response);
902
903                 strncpy(request.data.groupname, name,
904                         sizeof(request.data.groupname));
905                 request.data.groupname
906                         [sizeof(request.data.groupname) - 1] = '\0';
907
908                 ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
909                                                 &request, &response);
910
911                 if (ret == NSS_STATUS_SUCCESS) {
912                         ret = fill_grent(result, &response.data.gr,
913                                          (char *)response.extra_data.data,
914                                          &buffer, &buflen);
915
916                         if (ret == NSS_STATUS_TRYAGAIN) {
917                                 keep_response = true;
918                                 *errnop = errno = ERANGE;
919                                 goto done;
920                         }
921                 }
922
923         } else {
924
925                 /* We've been called again */
926
927                 ret = fill_grent(result, &response.data.gr,
928                                  (char *)response.extra_data.data, &buffer,
929                                  &buflen);
930
931                 if (ret == NSS_STATUS_TRYAGAIN) {
932                         keep_response = true;
933                         *errnop = errno = ERANGE;
934                         goto done;
935                 }
936
937                 keep_response = false;
938                 *errnop = 0;
939         }
940
941         winbindd_free_response(&response);
942         done:
943 #ifdef DEBUG_NSS
944         fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
945                 name, nss_err_str(ret), ret);
946 #endif
947
948 #if HAVE_PTHREAD
949         pthread_mutex_unlock(&winbind_nss_mutex);
950 #endif
951
952         return ret;
953 }
954
955 /* Return group struct from gid */
956
957 NSS_STATUS
958 _nss_winbind_getgrgid_r(gid_t gid,
959                         struct group *result, char *buffer,
960                         size_t buflen, int *errnop)
961 {
962         NSS_STATUS ret;
963         static struct winbindd_response response;
964         struct winbindd_request request;
965         static int keep_response;
966
967 #ifdef DEBUG_NSS
968         fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
969 #endif
970
971 #if HAVE_PTHREAD
972         pthread_mutex_lock(&winbind_nss_mutex);
973 #endif
974
975         /* If our static buffer needs to be expanded we are called again */
976         /* Or if the stored response group name differs from the request. */
977
978         if (!keep_response || gid != response.data.gr.gr_gid) {
979
980                 /* Call for the first time */
981
982                 ZERO_STRUCT(request);
983                 ZERO_STRUCT(response);
984
985                 request.data.gid = gid;
986
987                 ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
988                                                 &request, &response);
989
990                 if (ret == NSS_STATUS_SUCCESS) {
991
992                         ret = fill_grent(result, &response.data.gr,
993                                          (char *)response.extra_data.data,
994                                          &buffer, &buflen);
995
996                         if (ret == NSS_STATUS_TRYAGAIN) {
997                                 keep_response = true;
998                                 *errnop = errno = ERANGE;
999                                 goto done;
1000                         }
1001                 }
1002
1003         } else {
1004
1005                 /* We've been called again */
1006
1007                 ret = fill_grent(result, &response.data.gr,
1008                                  (char *)response.extra_data.data, &buffer,
1009                                  &buflen);
1010
1011                 if (ret == NSS_STATUS_TRYAGAIN) {
1012                         keep_response = true;
1013                         *errnop = errno = ERANGE;
1014                         goto done;
1015                 }
1016
1017                 keep_response = false;
1018                 *errnop = 0;
1019         }
1020
1021         winbindd_free_response(&response);
1022         done:
1023 #ifdef DEBUG_NSS
1024         fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
1025                 (unsigned int)gid, nss_err_str(ret), ret);
1026 #endif
1027
1028 #if HAVE_PTHREAD
1029         pthread_mutex_unlock(&winbind_nss_mutex);
1030 #endif
1031         return ret;
1032 }
1033
1034 /* Initialise supplementary groups */
1035
1036 NSS_STATUS
1037 _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
1038                             long int *size, gid_t **groups, long int limit,
1039                             int *errnop)
1040 {
1041         NSS_STATUS ret;
1042         struct winbindd_request request;
1043         struct winbindd_response response;
1044         int i;
1045
1046 #ifdef DEBUG_NSS
1047         fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
1048                 user, group);
1049 #endif
1050
1051 #if HAVE_PTHREAD
1052         pthread_mutex_lock(&winbind_nss_mutex);
1053 #endif
1054
1055         ZERO_STRUCT(request);
1056         ZERO_STRUCT(response);
1057
1058         strncpy(request.data.username, user,
1059                 sizeof(request.data.username) - 1);
1060
1061         ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
1062                                         &request, &response);
1063
1064         if (ret == NSS_STATUS_SUCCESS) {
1065                 int num_gids = response.data.num_entries;
1066                 gid_t *gid_list = (gid_t *)response.extra_data.data;
1067
1068 #ifdef DEBUG_NSS
1069                 fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
1070                                 "and %d gids\n", getpid(),
1071                                 user, num_gids);
1072 #endif
1073                 if (gid_list == NULL) {
1074                         ret = NSS_STATUS_NOTFOUND;
1075                         goto done;
1076                 }
1077
1078                 /* Copy group list to client */
1079
1080                 for (i = 0; i < num_gids; i++) {
1081
1082 #ifdef DEBUG_NSS
1083                         fprintf(stderr, "[%5d]: initgroups %s (%d): "
1084                                         "processing gid %d \n", getpid(),
1085                                         user, group, gid_list[i]);
1086 #endif
1087
1088                         /* Skip primary group */
1089
1090                         if (gid_list[i] == group) {
1091                                 continue;
1092                         }
1093
1094                         /* Skip groups without a mapping */
1095                         if (gid_list[i] == (uid_t)-1) {
1096                                 continue;
1097                         }
1098
1099                         /* Filled buffer ? If so, resize. */
1100
1101                         if (*start == *size) {
1102                                 long int newsize;
1103                                 gid_t *newgroups;
1104
1105                                 newsize = 2 * (*size);
1106                                 if (limit > 0) {
1107                                         if (*size == limit) {
1108                                                 goto done;
1109                                         }
1110                                         if (newsize > limit) {
1111                                                 newsize = limit;
1112                                         }
1113                                 }
1114
1115                                 newgroups = (gid_t *)
1116                                         realloc((*groups),
1117                                                 newsize * sizeof(**groups));
1118                                 if (!newgroups) {
1119                                         *errnop = ENOMEM;
1120                                         ret = NSS_STATUS_NOTFOUND;
1121                                         goto done;
1122                                 }
1123                                 *groups = newgroups;
1124                                 *size = newsize;
1125                         }
1126
1127                         /* Add to buffer */
1128
1129                         (*groups)[*start] = gid_list[i];
1130                         *start += 1;
1131                 }
1132         }
1133
1134         /* Back to your regularly scheduled programming */
1135
1136  done:
1137 #ifdef DEBUG_NSS
1138         fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1139                 user, nss_err_str(ret), ret);
1140 #endif
1141
1142 #if HAVE_PTHREAD
1143         pthread_mutex_unlock(&winbind_nss_mutex);
1144 #endif
1145
1146         return ret;
1147 }