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