r25493: Fix typo in Jeremy's thread-safe winbind patch:
[tprouty/samba.git] / source3 / 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 NSS_STATUS _nss_winbind_getusersids(const char *user_sid, char **group_sids, 
61                                     int *num_groups, char *buffer, size_t buf_size, 
62                                     int *errnop);
63 NSS_STATUS _nss_winbind_nametosid(const char *name, char **sid, char *buffer,
64                                   size_t buflen, int *errnop);
65 NSS_STATUS _nss_winbind_sidtoname(const char *sid, char **name, char *buffer, 
66                                   size_t buflen, int *errnop);
67 NSS_STATUS _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop);
68 NSS_STATUS _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop);
69 NSS_STATUS _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer, 
70                                  size_t buflen, int *errnop);
71 NSS_STATUS _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer, 
72                                  size_t buflen, int *errnop);
73
74 /* Prototypes from wb_common.c */
75
76 extern int winbindd_fd;
77
78 /* Allocate some space from the nss static buffer.  The buffer and buflen
79    are the pointers passed in by the C library to the _nss_ntdom_*
80    functions. */
81
82 static char *get_static(char **buffer, size_t *buflen, size_t len)
83 {
84         char *result;
85
86         /* Error check.  We return false if things aren't set up right, or
87            there isn't enough buffer space left. */
88         
89         if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
90                 return NULL;
91         }
92
93         /* Return an index into the static buffer */
94
95         result = *buffer;
96         *buffer += len;
97         *buflen -= len;
98
99         return result;
100 }
101
102 /* I've copied the strtok() replacement function next_token() from
103    lib/util_str.c as I really don't want to have to link in any other
104    objects if I can possibly avoid it. */
105
106 static bool next_token(char **ptr,char *buff,const char *sep, size_t bufsize)
107 {
108         char *s;
109         bool quoted;
110         size_t len=1;
111
112         if (!ptr) return false;
113
114         s = *ptr;
115
116         /* default to simple separators */
117         if (!sep) sep = " \t\n\r";
118
119         /* find the first non sep char */
120         while (*s && strchr(sep,*s)) s++;
121         
122         /* nothing left? */
123         if (! *s) return false;
124         
125         /* copy over the token */
126         for (quoted = false; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) {
127                 if (*s == '\"') {
128                         quoted = !quoted;
129                 } else {
130                         len++;
131                         *buff++ = *s;
132                 }
133         }
134         
135         *ptr = (*s) ? s+1 : s;  
136         *buff = 0;
137         
138         return true;
139 }
140
141
142 /* Fill a pwent structure from a winbindd_response structure.  We use
143    the static data passed to us by libc to put strings and stuff in.
144    Return NSS_STATUS_TRYAGAIN if we run out of memory. */
145
146 static NSS_STATUS fill_pwent(struct passwd *result,
147                                   struct winbindd_pw *pw,
148                                   char **buffer, size_t *buflen)
149 {
150         /* User name */
151
152         if ((result->pw_name = 
153              get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
154
155                 /* Out of memory */
156
157                 return NSS_STATUS_TRYAGAIN;
158         }
159
160         strcpy(result->pw_name, pw->pw_name);
161
162         /* Password */
163
164         if ((result->pw_passwd = 
165              get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
166
167                 /* Out of memory */
168
169                 return NSS_STATUS_TRYAGAIN;
170         }
171
172         strcpy(result->pw_passwd, pw->pw_passwd);
173         
174         /* [ug]id */
175
176         result->pw_uid = pw->pw_uid;
177         result->pw_gid = pw->pw_gid;
178
179         /* GECOS */
180
181         if ((result->pw_gecos = 
182              get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
183
184                 /* Out of memory */
185
186                 return NSS_STATUS_TRYAGAIN;
187         }
188
189         strcpy(result->pw_gecos, pw->pw_gecos);
190         
191         /* Home directory */
192         
193         if ((result->pw_dir = 
194              get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
195
196                 /* Out of memory */
197
198                 return NSS_STATUS_TRYAGAIN;
199         }
200
201         strcpy(result->pw_dir, pw->pw_dir);
202
203         /* Logon shell */
204         
205         if ((result->pw_shell = 
206              get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
207                 
208                 /* Out of memory */
209
210                 return NSS_STATUS_TRYAGAIN;
211         }
212
213         strcpy(result->pw_shell, pw->pw_shell);
214
215         /* The struct passwd for Solaris has some extra fields which must
216            be initialised or nscd crashes. */
217
218 #if HAVE_PASSWD_PW_COMMENT
219         result->pw_comment = "";
220 #endif
221
222 #if HAVE_PASSWD_PW_AGE
223         result->pw_age = "";
224 #endif
225
226         return NSS_STATUS_SUCCESS;
227 }
228
229 /* Fill a grent structure from a winbindd_response structure.  We use
230    the static data passed to us by libc to put strings and stuff in.
231    Return NSS_STATUS_TRYAGAIN if we run out of memory. */
232
233 static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
234                       char *gr_mem, char **buffer, size_t *buflen)
235 {
236         fstring name;
237         int i;
238         char *tst;
239
240         /* Group name */
241
242         if ((result->gr_name =
243              get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
244
245                 /* Out of memory */
246
247                 return NSS_STATUS_TRYAGAIN;
248         }
249
250         strcpy(result->gr_name, gr->gr_name);
251
252         /* Password */
253
254         if ((result->gr_passwd =
255              get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
256
257                 /* Out of memory */
258                 
259                 return NSS_STATUS_TRYAGAIN;
260         }
261
262         strcpy(result->gr_passwd, gr->gr_passwd);
263
264         /* gid */
265
266         result->gr_gid = gr->gr_gid;
267
268         /* Group membership */
269
270         if ((gr->num_gr_mem < 0) || !gr_mem) {
271                 gr->num_gr_mem = 0;
272         }
273
274         /* this next value is a pointer to a pointer so let's align it */
275
276         /* Calculate number of extra bytes needed to align on pointer size boundry */
277         if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
278                 i = sizeof(char*) - i;
279         
280         if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) * 
281                                  sizeof(char *)+i))) == NULL) {
282
283                 /* Out of memory */
284
285                 return NSS_STATUS_TRYAGAIN;
286         }
287         result->gr_mem = (char **)(tst + i);
288
289         if (gr->num_gr_mem == 0) {
290
291                 /* Group is empty */
292
293                 *(result->gr_mem) = NULL;
294                 return NSS_STATUS_SUCCESS;
295         }
296
297         /* Start looking at extra data */
298
299         i = 0;
300
301         while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) {
302         
303                 /* Allocate space for member */
304         
305                 if (((result->gr_mem)[i] = 
306                      get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
307             
308                         /* Out of memory */
309             
310                         return NSS_STATUS_TRYAGAIN;
311                 }        
312         
313                 strcpy((result->gr_mem)[i], name);
314                 i++;
315         }
316
317         /* Terminate list */
318
319         (result->gr_mem)[i] = NULL;
320
321         return NSS_STATUS_SUCCESS;
322 }
323
324 /*
325  * NSS user functions
326  */
327
328 static struct winbindd_response getpwent_response;
329
330 static int ndx_pw_cache;                 /* Current index into pwd cache */
331 static int num_pw_cache;                 /* Current size of pwd cache */
332
333 /* Rewind "file pointer" to start of ntdom password database */
334
335 NSS_STATUS
336 _nss_winbind_setpwent(void)
337 {
338         NSS_STATUS ret;
339 #ifdef DEBUG_NSS
340         fprintf(stderr, "[%5d]: setpwent\n", getpid());
341 #endif
342
343 #if HAVE_PTHREAD
344         pthread_mutex_lock(&winbind_nss_mutex);
345 #endif
346
347         if (num_pw_cache > 0) {
348                 ndx_pw_cache = num_pw_cache = 0;
349                 winbindd_free_response(&getpwent_response);
350         }
351
352         ret = winbindd_request_response(WINBINDD_SETPWENT, NULL, NULL);
353 #ifdef DEBUG_NSS
354         fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(),
355                 nss_err_str(ret), ret);
356 #endif
357
358 #if HAVE_PTHREAD
359         pthread_mutex_unlock(&winbind_nss_mutex);
360 #endif
361         return ret;
362 }
363
364 /* Close ntdom password database "file pointer" */
365
366 NSS_STATUS
367 _nss_winbind_endpwent(void)
368 {
369         NSS_STATUS ret;
370 #ifdef DEBUG_NSS
371         fprintf(stderr, "[%5d]: endpwent\n", getpid());
372 #endif
373
374 #if HAVE_PTHREAD
375         pthread_mutex_lock(&winbind_nss_mutex);
376 #endif
377
378         if (num_pw_cache > 0) {
379                 ndx_pw_cache = num_pw_cache = 0;
380                 winbindd_free_response(&getpwent_response);
381         }
382
383         ret = winbindd_request_response(WINBINDD_ENDPWENT, NULL, NULL);
384 #ifdef DEBUG_NSS
385         fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(),
386                 nss_err_str(ret), ret);
387 #endif
388
389 #if HAVE_PTHREAD
390         pthread_mutex_unlock(&winbind_nss_mutex);
391 #endif
392
393         return ret;
394 }
395
396 /* Fetch the next password entry from ntdom password database */
397
398 NSS_STATUS
399 _nss_winbind_getpwent_r(struct passwd *result, char *buffer, 
400                         size_t buflen, int *errnop)
401 {
402         NSS_STATUS ret;
403         struct winbindd_request request;
404         static int called_again;
405
406 #ifdef DEBUG_NSS
407         fprintf(stderr, "[%5d]: getpwent\n", getpid());
408 #endif
409
410 #if HAVE_PTHREAD
411         pthread_mutex_lock(&winbind_nss_mutex);
412 #endif
413
414         /* Return an entry from the cache if we have one, or if we are
415            called again because we exceeded our static buffer.  */
416
417         if ((ndx_pw_cache < num_pw_cache) || called_again) {
418                 goto return_result;
419         }
420
421         /* Else call winbindd to get a bunch of entries */
422         
423         if (num_pw_cache > 0) {
424                 winbindd_free_response(&getpwent_response);
425         }
426
427         ZERO_STRUCT(request);
428         ZERO_STRUCT(getpwent_response);
429
430         request.data.num_entries = MAX_GETPWENT_USERS;
431
432         ret = winbindd_request_response(WINBINDD_GETPWENT, &request, 
433                                &getpwent_response);
434
435         if (ret == NSS_STATUS_SUCCESS) {
436                 struct winbindd_pw *pw_cache;
437
438                 /* Fill cache */
439
440                 ndx_pw_cache = 0;
441                 num_pw_cache = getpwent_response.data.num_entries;
442
443                 /* Return a result */
444
445         return_result:
446
447                 pw_cache = (struct winbindd_pw *)
448                         getpwent_response.extra_data.data;
449
450                 /* Check data is valid */
451
452                 if (pw_cache == NULL) {
453                         ret = NSS_STATUS_NOTFOUND;
454                         goto done;
455                 }
456
457                 ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
458                                  &buffer, &buflen);
459                 
460                 /* Out of memory - try again */
461
462                 if (ret == NSS_STATUS_TRYAGAIN) {
463                         called_again = true;
464                         *errnop = errno = ERANGE;
465                         goto done;
466                 }
467
468                 *errnop = errno = 0;
469                 called_again = false;
470                 ndx_pw_cache++;
471
472                 /* If we've finished with this lot of results free cache */
473
474                 if (ndx_pw_cache == num_pw_cache) {
475                         ndx_pw_cache = num_pw_cache = 0;
476                         winbindd_free_response(&getpwent_response);
477                 }
478         }
479         done:
480 #ifdef DEBUG_NSS
481         fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(),
482                 nss_err_str(ret), ret);
483 #endif
484
485 #if HAVE_PTHREAD
486         pthread_mutex_unlock(&winbind_nss_mutex);
487 #endif
488         return ret;
489 }
490
491 /* Return passwd struct from uid */
492
493 NSS_STATUS
494 _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
495                         size_t buflen, int *errnop)
496 {
497         NSS_STATUS ret;
498         static struct winbindd_response response;
499         struct winbindd_request request;
500         static int keep_response;
501
502 #ifdef DEBUG_NSS
503         fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid);
504 #endif
505
506 #if HAVE_PTHREAD
507         pthread_mutex_lock(&winbind_nss_mutex);
508 #endif
509
510         /* If our static buffer needs to be expanded we are called again */
511         if (!keep_response || uid != response.data.pw.pw_uid) {
512
513                 /* Call for the first time */
514
515                 ZERO_STRUCT(response);
516                 ZERO_STRUCT(request);
517
518                 request.data.uid = uid;
519
520                 ret = winbindd_request_response(WINBINDD_GETPWUID, &request, &response);
521
522                 if (ret == NSS_STATUS_SUCCESS) {
523                         ret = fill_pwent(result, &response.data.pw, 
524                                          &buffer, &buflen);
525
526                         if (ret == NSS_STATUS_TRYAGAIN) {
527                                 keep_response = true;
528                                 *errnop = errno = ERANGE;
529                                 goto done;
530                         }
531                 }
532
533         } else {
534
535                 /* We've been called again */
536
537                 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
538
539                 if (ret == NSS_STATUS_TRYAGAIN) {
540                         *errnop = errno = ERANGE;
541                         goto done;
542                 }
543
544                 keep_response = false;
545                 *errnop = errno = 0;
546         }
547
548         winbindd_free_response(&response);
549
550         done:
551
552 #ifdef DEBUG_NSS
553         fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
554                 (unsigned int)uid, nss_err_str(ret), ret);
555 #endif
556
557 #if HAVE_PTHREAD
558         pthread_mutex_unlock(&winbind_nss_mutex);
559 #endif
560
561         return ret;
562 }
563
564 /* Return passwd struct from username */
565 NSS_STATUS
566 _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
567                         size_t buflen, int *errnop)
568 {
569         NSS_STATUS ret;
570         static struct winbindd_response response;
571         struct winbindd_request request;
572         static int keep_response;
573
574 #ifdef DEBUG_NSS
575         fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name);
576 #endif
577
578 #if HAVE_PTHREAD
579         pthread_mutex_lock(&winbind_nss_mutex);
580 #endif
581
582         /* If our static buffer needs to be expanded we are called again */
583
584         if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) {
585
586                 /* Call for the first time */
587
588                 ZERO_STRUCT(response);
589                 ZERO_STRUCT(request);
590
591                 strncpy(request.data.username, name, 
592                         sizeof(request.data.username) - 1);
593                 request.data.username
594                         [sizeof(request.data.username) - 1] = '\0';
595
596                 ret = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response);
597
598                 if (ret == NSS_STATUS_SUCCESS) {
599                         ret = fill_pwent(result, &response.data.pw, &buffer,
600                                          &buflen);
601
602                         if (ret == NSS_STATUS_TRYAGAIN) {
603                                 keep_response = true;
604                                 *errnop = errno = ERANGE;
605                                 goto done;
606                         }
607                 }
608
609         } else {
610
611                 /* We've been called again */
612
613                 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
614
615                 if (ret == NSS_STATUS_TRYAGAIN) {
616                         keep_response = true;
617                         *errnop = errno = ERANGE;
618                         goto done;
619                 }
620
621                 keep_response = false;
622                 *errnop = errno = 0;
623         }
624
625         winbindd_free_response(&response);
626         done:
627 #ifdef DEBUG_NSS
628         fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
629                 name, nss_err_str(ret), ret);
630 #endif
631
632 #if HAVE_PTHREAD
633         pthread_mutex_unlock(&winbind_nss_mutex);
634 #endif
635
636         return ret;
637 }
638
639 /*
640  * NSS group functions
641  */
642
643 static struct winbindd_response getgrent_response;
644
645 static int ndx_gr_cache;                 /* Current index into grp cache */
646 static int num_gr_cache;                 /* Current size of grp cache */
647
648 /* Rewind "file pointer" to start of ntdom group database */
649
650 NSS_STATUS
651 _nss_winbind_setgrent(void)
652 {
653         NSS_STATUS ret;
654 #ifdef DEBUG_NSS
655         fprintf(stderr, "[%5d]: setgrent\n", getpid());
656 #endif
657
658 #if HAVE_PTHREAD
659         pthread_mutex_lock(&winbind_nss_mutex);
660 #endif
661
662         if (num_gr_cache > 0) {
663                 ndx_gr_cache = num_gr_cache = 0;
664                 winbindd_free_response(&getgrent_response);
665         }
666
667         ret = winbindd_request_response(WINBINDD_SETGRENT, NULL, NULL);
668 #ifdef DEBUG_NSS
669         fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(),
670                 nss_err_str(ret), ret);
671 #endif
672
673 #if HAVE_PTHREAD
674         pthread_mutex_unlock(&winbind_nss_mutex);
675 #endif
676
677         return ret;
678 }
679
680 /* Close "file pointer" for ntdom group database */
681
682 NSS_STATUS
683 _nss_winbind_endgrent(void)
684 {
685         NSS_STATUS ret;
686 #ifdef DEBUG_NSS
687         fprintf(stderr, "[%5d]: endgrent\n", getpid());
688 #endif
689
690 #if HAVE_PTHREAD
691         pthread_mutex_lock(&winbind_nss_mutex);
692 #endif
693
694         if (num_gr_cache > 0) {
695                 ndx_gr_cache = num_gr_cache = 0;
696                 winbindd_free_response(&getgrent_response);
697         }
698
699         ret = winbindd_request_response(WINBINDD_ENDGRENT, NULL, NULL);
700 #ifdef DEBUG_NSS
701         fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(),
702                 nss_err_str(ret), ret);
703 #endif
704
705 #if HAVE_PTHREAD
706         pthread_mutex_unlock(&winbind_nss_mutex);
707 #endif
708
709         return ret;
710 }
711
712 /* Get next entry from ntdom group database */
713
714 static NSS_STATUS
715 winbind_getgrent(enum winbindd_cmd cmd,
716                  struct group *result,
717                  char *buffer, size_t buflen, int *errnop)
718 {
719         NSS_STATUS ret;
720         static struct winbindd_request request;
721         static int called_again;
722         
723
724 #ifdef DEBUG_NSS
725         fprintf(stderr, "[%5d]: getgrent\n", getpid());
726 #endif
727
728 #if HAVE_PTHREAD
729         pthread_mutex_lock(&winbind_nss_mutex);
730 #endif
731
732         /* Return an entry from the cache if we have one, or if we are
733            called again because we exceeded our static buffer.  */
734
735         if ((ndx_gr_cache < num_gr_cache) || called_again) {
736                 goto return_result;
737         }
738
739         /* Else call winbindd to get a bunch of entries */
740         
741         if (num_gr_cache > 0) {
742                 winbindd_free_response(&getgrent_response);
743         }
744
745         ZERO_STRUCT(request);
746         ZERO_STRUCT(getgrent_response);
747
748         request.data.num_entries = MAX_GETGRENT_USERS;
749
750         ret = winbindd_request_response(cmd, &request, 
751                                &getgrent_response);
752
753         if (ret == NSS_STATUS_SUCCESS) {
754                 struct winbindd_gr *gr_cache;
755                 int mem_ofs;
756
757                 /* Fill cache */
758
759                 ndx_gr_cache = 0;
760                 num_gr_cache = getgrent_response.data.num_entries;
761
762                 /* Return a result */
763
764         return_result:
765
766                 gr_cache = (struct winbindd_gr *)
767                         getgrent_response.extra_data.data;
768
769                 /* Check data is valid */
770
771                 if (gr_cache == NULL) {
772                         ret = NSS_STATUS_NOTFOUND;
773                         goto done;
774                 }
775
776                 /* Fill group membership.  The offset into the extra data
777                    for the group membership is the reported offset plus the
778                    size of all the winbindd_gr records returned. */
779
780                 mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
781                         num_gr_cache * sizeof(struct winbindd_gr);
782
783                 ret = fill_grent(result, &gr_cache[ndx_gr_cache],
784                                  ((char *)getgrent_response.extra_data.data)+mem_ofs,
785                                  &buffer, &buflen);
786                 
787                 /* Out of memory - try again */
788
789                 if (ret == NSS_STATUS_TRYAGAIN) {
790                         called_again = true;
791                         *errnop = errno = ERANGE;
792                         goto done;
793                 }
794
795                 *errnop = 0;
796                 called_again = false;
797                 ndx_gr_cache++;
798
799                 /* If we've finished with this lot of results free cache */
800
801                 if (ndx_gr_cache == num_gr_cache) {
802                         ndx_gr_cache = num_gr_cache = 0;
803                         winbindd_free_response(&getgrent_response);
804                 }
805         }
806         done:
807 #ifdef DEBUG_NSS
808         fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(),
809                 nss_err_str(ret), ret);
810 #endif
811
812 #if HAVE_PTHREAD
813         pthread_mutex_unlock(&winbind_nss_mutex);
814 #endif
815
816         return ret;
817 }
818
819
820 NSS_STATUS
821 _nss_winbind_getgrent_r(struct group *result,
822                         char *buffer, size_t buflen, int *errnop)
823 {
824         return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
825 }
826
827 NSS_STATUS
828 _nss_winbind_getgrlst_r(struct group *result,
829                         char *buffer, size_t buflen, int *errnop)
830 {
831         return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
832 }
833
834 /* Return group struct from group name */
835
836 NSS_STATUS
837 _nss_winbind_getgrnam_r(const char *name,
838                         struct group *result, char *buffer,
839                         size_t buflen, int *errnop)
840 {
841         NSS_STATUS ret;
842         static struct winbindd_response response;
843         struct winbindd_request request;
844         static int keep_response;
845         
846 #ifdef DEBUG_NSS
847         fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
848 #endif
849
850 #if HAVE_PTHREAD
851         pthread_mutex_lock(&winbind_nss_mutex);
852 #endif
853
854         /* If our static buffer needs to be expanded we are called again */
855         /* Or if the stored response group name differs from the request. */
856
857         if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) {
858
859                 /* Call for the first time */
860
861                 ZERO_STRUCT(request);
862                 ZERO_STRUCT(response);
863
864                 strncpy(request.data.groupname, name, 
865                         sizeof(request.data.groupname));
866                 request.data.groupname
867                         [sizeof(request.data.groupname) - 1] = '\0';
868
869                 ret = winbindd_request_response(WINBINDD_GETGRNAM, &request, &response);
870
871                 if (ret == NSS_STATUS_SUCCESS) {
872                         ret = fill_grent(result, &response.data.gr, 
873                                          (char *)response.extra_data.data,
874                                          &buffer, &buflen);
875
876                         if (ret == NSS_STATUS_TRYAGAIN) {
877                                 keep_response = true;
878                                 *errnop = errno = ERANGE;
879                                 goto done;
880                         }
881                 }
882
883         } else {
884
885                 /* We've been called again */
886
887                 ret = fill_grent(result, &response.data.gr, 
888                                  (char *)response.extra_data.data, &buffer,
889                                  &buflen);
890
891                 if (ret == NSS_STATUS_TRYAGAIN) {
892                         keep_response = true;
893                         *errnop = errno = ERANGE;
894                         goto done;
895                 }
896
897                 keep_response = false;
898                 *errnop = 0;
899         }
900
901         winbindd_free_response(&response);
902         done:
903 #ifdef DEBUG_NSS
904         fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
905                 name, nss_err_str(ret), ret);
906 #endif
907
908 #if HAVE_PTHREAD
909         pthread_mutex_unlock(&winbind_nss_mutex);
910 #endif
911
912         return ret;
913 }
914
915 /* Return group struct from gid */
916
917 NSS_STATUS
918 _nss_winbind_getgrgid_r(gid_t gid,
919                         struct group *result, char *buffer,
920                         size_t buflen, int *errnop)
921 {
922         NSS_STATUS ret;
923         static struct winbindd_response response;
924         struct winbindd_request request;
925         static int keep_response;
926
927 #ifdef DEBUG_NSS
928         fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
929 #endif
930
931 #if HAVE_PTHREAD
932         pthread_mutex_lock(&winbind_nss_mutex);
933 #endif
934
935         /* If our static buffer needs to be expanded we are called again */
936         /* Or if the stored response group name differs from the request. */
937
938         if (!keep_response || gid != response.data.gr.gr_gid) {
939
940                 /* Call for the first time */
941
942                 ZERO_STRUCT(request);
943                 ZERO_STRUCT(response);
944
945                 request.data.gid = gid;
946
947                 ret = winbindd_request_response(WINBINDD_GETGRGID, &request, &response);
948
949                 if (ret == NSS_STATUS_SUCCESS) {
950
951                         ret = fill_grent(result, &response.data.gr, 
952                                          (char *)response.extra_data.data, 
953                                          &buffer, &buflen);
954
955                         if (ret == NSS_STATUS_TRYAGAIN) {
956                                 keep_response = true;
957                                 *errnop = errno = ERANGE;
958                                 goto done;
959                         }
960                 }
961
962         } else {
963
964                 /* We've been called again */
965
966                 ret = fill_grent(result, &response.data.gr, 
967                                  (char *)response.extra_data.data, &buffer,
968                                  &buflen);
969
970                 if (ret == NSS_STATUS_TRYAGAIN) {
971                         keep_response = true;
972                         *errnop = errno = ERANGE;
973                         goto done;
974                 }
975
976                 keep_response = false;
977                 *errnop = 0;
978         }
979
980         winbindd_free_response(&response);
981         done:
982 #ifdef DEBUG_NSS
983         fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
984                 (unsigned int)gid, nss_err_str(ret), ret);
985 #endif
986
987 #if HAVE_PTHREAD
988         pthread_mutex_unlock(&winbind_nss_mutex);
989 #endif
990         return ret;
991 }
992
993 /* Initialise supplementary groups */
994
995 NSS_STATUS
996 _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
997                             long int *size, gid_t **groups, long int limit,
998                             int *errnop)
999 {
1000         NSS_STATUS ret;
1001         struct winbindd_request request;
1002         struct winbindd_response response;
1003         int i;
1004
1005 #ifdef DEBUG_NSS
1006         fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
1007                 user, group);
1008 #endif
1009
1010 #if HAVE_PTHREAD
1011         pthread_mutex_lock(&winbind_nss_mutex);
1012 #endif
1013
1014         ZERO_STRUCT(request);
1015         ZERO_STRUCT(response);
1016
1017         strncpy(request.data.username, user,
1018                 sizeof(request.data.username) - 1);
1019
1020         ret = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
1021
1022         if (ret == NSS_STATUS_SUCCESS) {
1023                 int num_gids = response.data.num_entries;
1024                 gid_t *gid_list = (gid_t *)response.extra_data.data;
1025
1026 #ifdef DEBUG_NSS
1027                 fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
1028                                 "and %d gids\n", getpid(),
1029                                 user, num_gids);
1030 #endif
1031                 if (gid_list == NULL) {
1032                         ret = NSS_STATUS_NOTFOUND;
1033                         goto done;
1034                 }
1035
1036                 /* Copy group list to client */
1037
1038                 for (i = 0; i < num_gids; i++) {
1039
1040 #ifdef DEBUG_NSS
1041                         fprintf(stderr, "[%5d]: initgroups %s (%d): "
1042                                         "processing gid %d \n", getpid(),
1043                                         user, group, gid_list[i]);
1044 #endif
1045
1046                         /* Skip primary group */
1047
1048                         if (gid_list[i] == group) {
1049                                 continue;
1050                         }
1051
1052                         /* Filled buffer ? If so, resize. */
1053
1054                         if (*start == *size) {
1055                                 long int newsize;
1056                                 gid_t *newgroups;
1057
1058                                 newsize = 2 * (*size);
1059                                 if (limit > 0) {
1060                                         if (*size == limit) {
1061                                                 goto done;
1062                                         }
1063                                         if (newsize > limit) {
1064                                                 newsize = limit;
1065                                         }
1066                                 }
1067
1068                                 newgroups = (gid_t *)
1069                                         realloc((*groups),
1070                                                 newsize * sizeof(**groups));
1071                                 if (!newgroups) {
1072                                         *errnop = ENOMEM;
1073                                         ret = NSS_STATUS_NOTFOUND;
1074                                         goto done;
1075                                 }
1076                                 *groups = newgroups;
1077                                 *size = newsize;
1078                         }
1079
1080                         /* Add to buffer */
1081
1082                         (*groups)[*start] = gid_list[i];
1083                         *start += 1;
1084                 }
1085         }
1086         
1087         /* Back to your regularly scheduled programming */
1088
1089  done:
1090 #ifdef DEBUG_NSS
1091         fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1092                 user, nss_err_str(ret), ret);
1093 #endif
1094
1095 #if HAVE_PTHREAD
1096         pthread_mutex_unlock(&winbind_nss_mutex);
1097 #endif
1098
1099         return ret;
1100 }
1101
1102
1103 /* return a list of group SIDs for a user SID */
1104 NSS_STATUS
1105 _nss_winbind_getusersids(const char *user_sid, char **group_sids,
1106                          int *num_groups,
1107                          char *buffer, size_t buf_size, int *errnop)
1108 {
1109         NSS_STATUS ret;
1110         struct winbindd_request request;
1111         struct winbindd_response response;
1112
1113 #ifdef DEBUG_NSS
1114         fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid);
1115 #endif
1116
1117 #if HAVE_PTHREAD
1118         pthread_mutex_lock(&winbind_nss_mutex);
1119 #endif
1120
1121         ZERO_STRUCT(request);
1122         ZERO_STRUCT(response);
1123
1124         strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1);
1125         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1126
1127         ret = winbindd_request_response(WINBINDD_GETUSERSIDS, &request, &response);
1128
1129         if (ret != NSS_STATUS_SUCCESS) {
1130                 goto done;
1131         }
1132
1133         if (buf_size < response.length - sizeof(response)) {
1134                 ret = NSS_STATUS_TRYAGAIN;
1135                 errno = *errnop = ERANGE;
1136                 goto done;
1137         }
1138
1139         *num_groups = response.data.num_entries;
1140         *group_sids = buffer;
1141         memcpy(buffer, response.extra_data.data, response.length - sizeof(response));
1142         errno = *errnop = 0;
1143         
1144  done:
1145         winbindd_free_response(&response);
1146
1147 #if HAVE_PTHREAD
1148         pthread_mutex_unlock(&winbind_nss_mutex);
1149 #endif
1150
1151         return ret;
1152 }
1153
1154
1155 /* map a user or group name to a SID string */
1156 NSS_STATUS
1157 _nss_winbind_nametosid(const char *name, char **sid, char *buffer,
1158                        size_t buflen, int *errnop)
1159 {
1160         NSS_STATUS ret;
1161         struct winbindd_response response;
1162         struct winbindd_request request;
1163
1164 #ifdef DEBUG_NSS
1165         fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name);
1166 #endif
1167
1168 #if HAVE_PTHREAD
1169         pthread_mutex_lock(&winbind_nss_mutex);
1170 #endif
1171
1172         ZERO_STRUCT(response);
1173         ZERO_STRUCT(request);
1174
1175         strncpy(request.data.name.name, name, 
1176                 sizeof(request.data.name.name) - 1);
1177         request.data.name.name[sizeof(request.data.name.name) - 1] = '\0';
1178
1179         ret = winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response);
1180         if (ret != NSS_STATUS_SUCCESS) {
1181                 *errnop = errno = EINVAL;
1182                 goto failed;
1183         }
1184
1185         if (buflen < strlen(response.data.sid.sid)+1) {
1186                 ret = NSS_STATUS_TRYAGAIN;
1187                 *errnop = errno = ERANGE;
1188                 goto failed;
1189         }
1190
1191         *errnop = errno = 0;
1192         *sid = buffer;
1193         strcpy(*sid, response.data.sid.sid);
1194
1195 failed:
1196         winbindd_free_response(&response);
1197
1198 #if HAVE_PTHREAD
1199         pthread_mutex_unlock(&winbind_nss_mutex);
1200 #endif
1201
1202         return ret;
1203 }
1204
1205 /* map a sid string to a user or group name */
1206 NSS_STATUS
1207 _nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
1208                        size_t buflen, int *errnop)
1209 {
1210         NSS_STATUS ret;
1211         struct winbindd_response response;
1212         struct winbindd_request request;
1213         static char sep_char;
1214         unsigned needed;
1215
1216 #ifdef DEBUG_NSS
1217         fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid);
1218 #endif
1219
1220 #if HAVE_PTHREAD
1221         pthread_mutex_lock(&winbind_nss_mutex);
1222 #endif
1223
1224         ZERO_STRUCT(response);
1225         ZERO_STRUCT(request);
1226
1227         /* we need to fetch the separator first time through */
1228         if (!sep_char) {
1229                 ret = winbindd_request_response(WINBINDD_INFO, &request, &response);
1230                 if (ret != NSS_STATUS_SUCCESS) {
1231                         *errnop = errno = EINVAL;
1232                         goto failed;
1233                 }
1234
1235                 sep_char = response.data.info.winbind_separator;
1236                 winbindd_free_response(&response);
1237         }
1238
1239
1240         strncpy(request.data.sid, sid, 
1241                 sizeof(request.data.sid) - 1);
1242         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1243
1244         ret = winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response);
1245         if (ret != NSS_STATUS_SUCCESS) {
1246                 *errnop = errno = EINVAL;
1247                 goto failed;
1248         }
1249
1250         needed = 
1251                 strlen(response.data.name.dom_name) +
1252                 strlen(response.data.name.name) + 2;
1253
1254         if (buflen < needed) {
1255                 ret = NSS_STATUS_TRYAGAIN;
1256                 *errnop = errno = ERANGE;
1257                 goto failed;
1258         }
1259
1260         snprintf(buffer, needed, "%s%c%s", 
1261                  response.data.name.dom_name,
1262                  sep_char,
1263                  response.data.name.name);
1264
1265         *name = buffer;
1266         *errnop = errno = 0;
1267
1268 failed:
1269         winbindd_free_response(&response);
1270
1271 #if HAVE_PTHREAD
1272         pthread_mutex_unlock(&winbind_nss_mutex);
1273 #endif
1274
1275         return ret;
1276 }
1277
1278 /* map a sid to a uid */
1279 NSS_STATUS
1280 _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop)
1281 {
1282         NSS_STATUS ret;
1283         struct winbindd_response response;
1284         struct winbindd_request request;
1285
1286 #ifdef DEBUG_NSS
1287         fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid);
1288 #endif
1289
1290 #if HAVE_PTHREAD
1291         pthread_mutex_lock(&winbind_nss_mutex);
1292 #endif
1293
1294         ZERO_STRUCT(request);
1295         ZERO_STRUCT(response);
1296
1297         strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
1298         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1299
1300         ret = winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response);
1301         if (ret != NSS_STATUS_SUCCESS) {
1302                 *errnop = errno = EINVAL;
1303                 goto failed;
1304         }
1305
1306         *uid = response.data.uid;
1307
1308 failed:
1309
1310 #if HAVE_PTHREAD
1311         pthread_mutex_unlock(&winbind_nss_mutex);
1312 #endif
1313
1314         return ret;
1315 }
1316
1317 /* map a sid to a gid */
1318 NSS_STATUS
1319 _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop)
1320 {
1321         NSS_STATUS ret;
1322         struct winbindd_response response;
1323         struct winbindd_request request;
1324
1325 #ifdef DEBUG_NSS
1326         fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid);
1327 #endif
1328
1329 #if HAVE_PTHREAD
1330         pthread_mutex_lock(&winbind_nss_mutex);
1331 #endif
1332
1333         ZERO_STRUCT(request);
1334         ZERO_STRUCT(response);
1335
1336         strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
1337         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1338
1339         ret = winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response);
1340         if (ret != NSS_STATUS_SUCCESS) {
1341                 *errnop = errno = EINVAL;
1342                 goto failed;
1343         }
1344
1345         *gid = response.data.gid;
1346
1347 failed:
1348
1349 #if HAVE_PTHREAD
1350         pthread_mutex_unlock(&winbind_nss_mutex);
1351 #endif
1352
1353         return ret;
1354 }
1355
1356 /* map a uid to a SID string */
1357 NSS_STATUS
1358 _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
1359                       size_t buflen, int *errnop)
1360 {
1361         NSS_STATUS ret;
1362         struct winbindd_response response;
1363         struct winbindd_request request;
1364
1365 #ifdef DEBUG_NSS
1366         fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid);
1367 #endif
1368
1369 #if HAVE_PTHREAD
1370         pthread_mutex_lock(&winbind_nss_mutex);
1371 #endif
1372
1373         ZERO_STRUCT(response);
1374         ZERO_STRUCT(request);
1375
1376         request.data.uid = uid;
1377
1378         ret = winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response);
1379         if (ret != NSS_STATUS_SUCCESS) {
1380                 *errnop = errno = EINVAL;
1381                 goto failed;
1382         }
1383
1384         if (buflen < strlen(response.data.sid.sid)+1) {
1385                 ret = NSS_STATUS_TRYAGAIN;
1386                 *errnop = errno = ERANGE;
1387                 goto failed;
1388         }
1389
1390         *errnop = errno = 0;
1391         *sid = buffer;
1392         strcpy(*sid, response.data.sid.sid);
1393
1394 failed:
1395         winbindd_free_response(&response);
1396
1397 #if HAVE_PTHREAD
1398         pthread_mutex_unlock(&winbind_nss_mutex);
1399 #endif
1400
1401         return ret;
1402 }
1403
1404 /* map a gid to a SID string */
1405 NSS_STATUS
1406 _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
1407                       size_t buflen, int *errnop)
1408 {
1409         NSS_STATUS ret;
1410         struct winbindd_response response;
1411         struct winbindd_request request;
1412
1413 #ifdef DEBUG_NSS
1414         fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid);
1415 #endif
1416
1417 #if HAVE_PTHREAD
1418         pthread_mutex_lock(&winbind_nss_mutex);
1419 #endif
1420
1421         ZERO_STRUCT(response);
1422         ZERO_STRUCT(request);
1423
1424         request.data.gid = gid;
1425
1426         ret = winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response);
1427         if (ret != NSS_STATUS_SUCCESS) {
1428                 *errnop = errno = EINVAL;
1429                 goto failed;
1430         }
1431
1432         if (buflen < strlen(response.data.sid.sid)+1) {
1433                 ret = NSS_STATUS_TRYAGAIN;
1434                 *errnop = errno = ERANGE;
1435                 goto failed;
1436         }
1437
1438         *errnop = errno = 0;
1439         *sid = buffer;
1440         strcpy(*sid, response.data.sid.sid);
1441
1442 failed:
1443         winbindd_free_response(&response);
1444
1445 #if HAVE_PTHREAD
1446         pthread_mutex_unlock(&winbind_nss_mutex);
1447 #endif
1448
1449         return ret;
1450 }