s3-libads: Move to using only the HAVE_KRB5 define
[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                       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->num_gr_mem < 0) || !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                         /* Filled buffer ? If so, resize. */
1105
1106                         if (*start == *size) {
1107                                 long int newsize;
1108                                 gid_t *newgroups;
1109
1110                                 newsize = 2 * (*size);
1111                                 if (limit > 0) {
1112                                         if (*size == limit) {
1113                                                 goto done;
1114                                         }
1115                                         if (newsize > limit) {
1116                                                 newsize = limit;
1117                                         }
1118                                 }
1119
1120                                 newgroups = (gid_t *)
1121                                         realloc((*groups),
1122                                                 newsize * sizeof(**groups));
1123                                 if (!newgroups) {
1124                                         *errnop = ENOMEM;
1125                                         ret = NSS_STATUS_NOTFOUND;
1126                                         goto done;
1127                                 }
1128                                 *groups = newgroups;
1129                                 *size = newsize;
1130                         }
1131
1132                         /* Add to buffer */
1133
1134                         (*groups)[*start] = gid_list[i];
1135                         *start += 1;
1136                 }
1137         }
1138
1139         /* Back to your regularly scheduled programming */
1140
1141  done:
1142 #ifdef DEBUG_NSS
1143         fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1144                 user, nss_err_str(ret), ret);
1145 #endif
1146
1147 #if HAVE_PTHREAD
1148         pthread_mutex_unlock(&winbind_nss_mutex);
1149 #endif
1150
1151         return ret;
1152 }
1153
1154
1155 /* return a list of group SIDs for a user SID */
1156 NSS_STATUS
1157 _nss_winbind_getusersids(const char *user_sid, char **group_sids,
1158                          int *num_groups,
1159                          char *buffer, size_t buf_size, int *errnop)
1160 {
1161         NSS_STATUS ret;
1162         struct winbindd_request request;
1163         struct winbindd_response response;
1164
1165 #ifdef DEBUG_NSS
1166         fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid);
1167 #endif
1168
1169 #if HAVE_PTHREAD
1170         pthread_mutex_lock(&winbind_nss_mutex);
1171 #endif
1172
1173         ZERO_STRUCT(request);
1174         ZERO_STRUCT(response);
1175
1176         strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1);
1177         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1178
1179         ret = winbindd_request_response(WINBINDD_GETUSERSIDS, &request, &response);
1180
1181         if (ret != NSS_STATUS_SUCCESS) {
1182                 goto done;
1183         }
1184
1185         if (buf_size < response.length - sizeof(response)) {
1186                 ret = NSS_STATUS_TRYAGAIN;
1187                 errno = *errnop = ERANGE;
1188                 goto done;
1189         }
1190
1191         *num_groups = response.data.num_entries;
1192         *group_sids = buffer;
1193         memcpy(buffer, response.extra_data.data, response.length - sizeof(response));
1194         errno = *errnop = 0;
1195
1196  done:
1197         winbindd_free_response(&response);
1198
1199 #if HAVE_PTHREAD
1200         pthread_mutex_unlock(&winbind_nss_mutex);
1201 #endif
1202
1203         return ret;
1204 }
1205
1206
1207 /* map a user or group name to a SID string */
1208 NSS_STATUS
1209 _nss_winbind_nametosid(const char *name, char **sid, char *buffer,
1210                        size_t buflen, int *errnop)
1211 {
1212         NSS_STATUS ret;
1213         struct winbindd_response response;
1214         struct winbindd_request request;
1215
1216 #ifdef DEBUG_NSS
1217         fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name);
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         strncpy(request.data.name.name, name,
1228                 sizeof(request.data.name.name) - 1);
1229         request.data.name.name[sizeof(request.data.name.name) - 1] = '\0';
1230
1231         ret = winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response);
1232         if (ret != NSS_STATUS_SUCCESS) {
1233                 *errnop = errno = EINVAL;
1234                 goto failed;
1235         }
1236
1237         if (buflen < strlen(response.data.sid.sid)+1) {
1238                 ret = NSS_STATUS_TRYAGAIN;
1239                 *errnop = errno = ERANGE;
1240                 goto failed;
1241         }
1242
1243         *errnop = errno = 0;
1244         *sid = buffer;
1245         strcpy(*sid, response.data.sid.sid);
1246
1247 failed:
1248         winbindd_free_response(&response);
1249
1250 #if HAVE_PTHREAD
1251         pthread_mutex_unlock(&winbind_nss_mutex);
1252 #endif
1253
1254         return ret;
1255 }
1256
1257 /* map a sid string to a user or group name */
1258 NSS_STATUS
1259 _nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
1260                        size_t buflen, int *errnop)
1261 {
1262         NSS_STATUS ret;
1263         struct winbindd_response response;
1264         struct winbindd_request request;
1265         static char sep_char;
1266         unsigned needed;
1267
1268 #ifdef DEBUG_NSS
1269         fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid);
1270 #endif
1271
1272 #if HAVE_PTHREAD
1273         pthread_mutex_lock(&winbind_nss_mutex);
1274 #endif
1275
1276         ZERO_STRUCT(response);
1277         ZERO_STRUCT(request);
1278
1279         /* we need to fetch the separator first time through */
1280         if (!sep_char) {
1281                 ret = winbindd_request_response(WINBINDD_INFO, &request, &response);
1282                 if (ret != NSS_STATUS_SUCCESS) {
1283                         *errnop = errno = EINVAL;
1284                         goto failed;
1285                 }
1286
1287                 sep_char = response.data.info.winbind_separator;
1288                 winbindd_free_response(&response);
1289         }
1290
1291
1292         strncpy(request.data.sid, sid,
1293                 sizeof(request.data.sid) - 1);
1294         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1295
1296         ret = winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response);
1297         if (ret != NSS_STATUS_SUCCESS) {
1298                 *errnop = errno = EINVAL;
1299                 goto failed;
1300         }
1301
1302         needed =
1303                 strlen(response.data.name.dom_name) +
1304                 strlen(response.data.name.name) + 2;
1305
1306         if (buflen < needed) {
1307                 ret = NSS_STATUS_TRYAGAIN;
1308                 *errnop = errno = ERANGE;
1309                 goto failed;
1310         }
1311
1312         snprintf(buffer, needed, "%s%c%s",
1313                  response.data.name.dom_name,
1314                  sep_char,
1315                  response.data.name.name);
1316
1317         *name = buffer;
1318         *errnop = errno = 0;
1319
1320 failed:
1321         winbindd_free_response(&response);
1322
1323 #if HAVE_PTHREAD
1324         pthread_mutex_unlock(&winbind_nss_mutex);
1325 #endif
1326
1327         return ret;
1328 }
1329
1330 /* map a sid to a uid */
1331 NSS_STATUS
1332 _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop)
1333 {
1334         NSS_STATUS ret;
1335         struct winbindd_response response;
1336         struct winbindd_request request;
1337
1338 #ifdef DEBUG_NSS
1339         fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid);
1340 #endif
1341
1342 #if HAVE_PTHREAD
1343         pthread_mutex_lock(&winbind_nss_mutex);
1344 #endif
1345
1346         ZERO_STRUCT(request);
1347         ZERO_STRUCT(response);
1348
1349         strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
1350         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1351
1352         ret = winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response);
1353         if (ret != NSS_STATUS_SUCCESS) {
1354                 *errnop = errno = EINVAL;
1355                 goto failed;
1356         }
1357
1358         *uid = response.data.uid;
1359
1360 failed:
1361
1362 #if HAVE_PTHREAD
1363         pthread_mutex_unlock(&winbind_nss_mutex);
1364 #endif
1365
1366         return ret;
1367 }
1368
1369 /* map a sid to a gid */
1370 NSS_STATUS
1371 _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop)
1372 {
1373         NSS_STATUS ret;
1374         struct winbindd_response response;
1375         struct winbindd_request request;
1376
1377 #ifdef DEBUG_NSS
1378         fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid);
1379 #endif
1380
1381 #if HAVE_PTHREAD
1382         pthread_mutex_lock(&winbind_nss_mutex);
1383 #endif
1384
1385         ZERO_STRUCT(request);
1386         ZERO_STRUCT(response);
1387
1388         strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
1389         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1390
1391         ret = winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response);
1392         if (ret != NSS_STATUS_SUCCESS) {
1393                 *errnop = errno = EINVAL;
1394                 goto failed;
1395         }
1396
1397         *gid = response.data.gid;
1398
1399 failed:
1400
1401 #if HAVE_PTHREAD
1402         pthread_mutex_unlock(&winbind_nss_mutex);
1403 #endif
1404
1405         return ret;
1406 }
1407
1408 /* map a uid to a SID string */
1409 NSS_STATUS
1410 _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
1411                       size_t buflen, int *errnop)
1412 {
1413         NSS_STATUS ret;
1414         struct winbindd_response response;
1415         struct winbindd_request request;
1416
1417 #ifdef DEBUG_NSS
1418         fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid);
1419 #endif
1420
1421 #if HAVE_PTHREAD
1422         pthread_mutex_lock(&winbind_nss_mutex);
1423 #endif
1424
1425         ZERO_STRUCT(response);
1426         ZERO_STRUCT(request);
1427
1428         request.data.uid = uid;
1429
1430         ret = winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response);
1431         if (ret != NSS_STATUS_SUCCESS) {
1432                 *errnop = errno = EINVAL;
1433                 goto failed;
1434         }
1435
1436         if (buflen < strlen(response.data.sid.sid)+1) {
1437                 ret = NSS_STATUS_TRYAGAIN;
1438                 *errnop = errno = ERANGE;
1439                 goto failed;
1440         }
1441
1442         *errnop = errno = 0;
1443         *sid = buffer;
1444         strcpy(*sid, response.data.sid.sid);
1445
1446 failed:
1447         winbindd_free_response(&response);
1448
1449 #if HAVE_PTHREAD
1450         pthread_mutex_unlock(&winbind_nss_mutex);
1451 #endif
1452
1453         return ret;
1454 }
1455
1456 /* map a gid to a SID string */
1457 NSS_STATUS
1458 _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
1459                       size_t buflen, int *errnop)
1460 {
1461         NSS_STATUS ret;
1462         struct winbindd_response response;
1463         struct winbindd_request request;
1464
1465 #ifdef DEBUG_NSS
1466         fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid);
1467 #endif
1468
1469 #if HAVE_PTHREAD
1470         pthread_mutex_lock(&winbind_nss_mutex);
1471 #endif
1472
1473         ZERO_STRUCT(response);
1474         ZERO_STRUCT(request);
1475
1476         request.data.gid = gid;
1477
1478         ret = winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response);
1479         if (ret != NSS_STATUS_SUCCESS) {
1480                 *errnop = errno = EINVAL;
1481                 goto failed;
1482         }
1483
1484         if (buflen < strlen(response.data.sid.sid)+1) {
1485                 ret = NSS_STATUS_TRYAGAIN;
1486                 *errnop = errno = ERANGE;
1487                 goto failed;
1488         }
1489
1490         *errnop = errno = 0;
1491         *sid = buffer;
1492         strcpy(*sid, response.data.sid.sid);
1493
1494 failed:
1495         winbindd_free_response(&response);
1496
1497 #if HAVE_PTHREAD
1498         pthread_mutex_unlock(&winbind_nss_mutex);
1499 #endif
1500
1501         return ret;
1502 }