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