libreplace: Fix the build on Solaris
[sfrench/samba-autobuild/.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(NULL, 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(NULL, 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(NULL, 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(NULL, 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(NULL, 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(NULL, 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(NULL, 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(NULL, 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(NULL, WINBINDD_GETGRNAM,
922                                                 &request, &response);
923
924                 if (ret == NSS_STATUS_SUCCESS) {
925                         ret = fill_grent(result, &response.data.gr,
926                                          (char *)response.extra_data.data,
927                                          &buffer, &buflen);
928
929                         if (ret == NSS_STATUS_TRYAGAIN) {
930                                 keep_response = true;
931                                 *errnop = errno = ERANGE;
932                                 goto done;
933                         }
934                 }
935
936         } else {
937
938                 /* We've been called again */
939
940                 ret = fill_grent(result, &response.data.gr,
941                                  (char *)response.extra_data.data, &buffer,
942                                  &buflen);
943
944                 if (ret == NSS_STATUS_TRYAGAIN) {
945                         keep_response = true;
946                         *errnop = errno = ERANGE;
947                         goto done;
948                 }
949
950                 keep_response = false;
951                 *errnop = 0;
952         }
953
954         winbindd_free_response(&response);
955         done:
956 #ifdef DEBUG_NSS
957         fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
958                 name, nss_err_str(ret), ret);
959 #endif
960
961 #if HAVE_PTHREAD
962         pthread_mutex_unlock(&winbind_nss_mutex);
963 #endif
964
965         return ret;
966 }
967
968 /* Return group struct from gid */
969
970 NSS_STATUS
971 _nss_winbind_getgrgid_r(gid_t gid,
972                         struct group *result, char *buffer,
973                         size_t buflen, int *errnop)
974 {
975         NSS_STATUS ret;
976         static struct winbindd_response response;
977         struct winbindd_request request;
978         static int keep_response;
979
980 #ifdef DEBUG_NSS
981         fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
982 #endif
983
984 #if HAVE_PTHREAD
985         pthread_mutex_lock(&winbind_nss_mutex);
986 #endif
987
988         /* If our static buffer needs to be expanded we are called again */
989         /* Or if the stored response group name differs from the request. */
990
991         if (!keep_response || gid != response.data.gr.gr_gid) {
992
993                 /* Call for the first time */
994
995                 ZERO_STRUCT(request);
996                 ZERO_STRUCT(response);
997
998                 request.data.gid = gid;
999
1000                 ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
1001                                                 &request, &response);
1002
1003                 if (ret == NSS_STATUS_SUCCESS) {
1004
1005                         ret = fill_grent(result, &response.data.gr,
1006                                          (char *)response.extra_data.data,
1007                                          &buffer, &buflen);
1008
1009                         if (ret == NSS_STATUS_TRYAGAIN) {
1010                                 keep_response = true;
1011                                 *errnop = errno = ERANGE;
1012                                 goto done;
1013                         }
1014                 }
1015
1016         } else {
1017
1018                 /* We've been called again */
1019
1020                 ret = fill_grent(result, &response.data.gr,
1021                                  (char *)response.extra_data.data, &buffer,
1022                                  &buflen);
1023
1024                 if (ret == NSS_STATUS_TRYAGAIN) {
1025                         keep_response = true;
1026                         *errnop = errno = ERANGE;
1027                         goto done;
1028                 }
1029
1030                 keep_response = false;
1031                 *errnop = 0;
1032         }
1033
1034         winbindd_free_response(&response);
1035         done:
1036 #ifdef DEBUG_NSS
1037         fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
1038                 (unsigned int)gid, nss_err_str(ret), ret);
1039 #endif
1040
1041 #if HAVE_PTHREAD
1042         pthread_mutex_unlock(&winbind_nss_mutex);
1043 #endif
1044         return ret;
1045 }
1046
1047 /* Initialise supplementary groups */
1048
1049 NSS_STATUS
1050 _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
1051                             long int *size, gid_t **groups, long int limit,
1052                             int *errnop)
1053 {
1054         NSS_STATUS ret;
1055         struct winbindd_request request;
1056         struct winbindd_response response;
1057         int i;
1058
1059 #ifdef DEBUG_NSS
1060         fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
1061                 user, group);
1062 #endif
1063
1064 #if HAVE_PTHREAD
1065         pthread_mutex_lock(&winbind_nss_mutex);
1066 #endif
1067
1068         ZERO_STRUCT(request);
1069         ZERO_STRUCT(response);
1070
1071         strncpy(request.data.username, user,
1072                 sizeof(request.data.username) - 1);
1073
1074         ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
1075                                         &request, &response);
1076
1077         if (ret == NSS_STATUS_SUCCESS) {
1078                 int num_gids = response.data.num_entries;
1079                 gid_t *gid_list = (gid_t *)response.extra_data.data;
1080
1081 #ifdef DEBUG_NSS
1082                 fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
1083                                 "and %d gids\n", getpid(),
1084                                 user, num_gids);
1085 #endif
1086                 if (gid_list == NULL) {
1087                         ret = NSS_STATUS_NOTFOUND;
1088                         goto done;
1089                 }
1090
1091                 /* Copy group list to client */
1092
1093                 for (i = 0; i < num_gids; i++) {
1094
1095 #ifdef DEBUG_NSS
1096                         fprintf(stderr, "[%5d]: initgroups %s (%d): "
1097                                         "processing gid %d \n", getpid(),
1098                                         user, group, gid_list[i]);
1099 #endif
1100
1101                         /* Skip primary group */
1102
1103                         if (gid_list[i] == group) {
1104                                 continue;
1105                         }
1106
1107                         /* Skip groups without a mapping */
1108                         if (gid_list[i] == (uid_t)-1) {
1109                                 continue;
1110                         }
1111
1112                         /* Filled buffer ? If so, resize. */
1113
1114                         if (*start == *size) {
1115                                 long int newsize;
1116                                 gid_t *newgroups;
1117
1118                                 newsize = 2 * (*size);
1119                                 if (limit > 0) {
1120                                         if (*size == limit) {
1121                                                 goto done;
1122                                         }
1123                                         if (newsize > limit) {
1124                                                 newsize = limit;
1125                                         }
1126                                 }
1127
1128                                 newgroups = (gid_t *)
1129                                         realloc((*groups),
1130                                                 newsize * sizeof(**groups));
1131                                 if (!newgroups) {
1132                                         *errnop = ENOMEM;
1133                                         ret = NSS_STATUS_NOTFOUND;
1134                                         goto done;
1135                                 }
1136                                 *groups = newgroups;
1137                                 *size = newsize;
1138                         }
1139
1140                         /* Add to buffer */
1141
1142                         (*groups)[*start] = gid_list[i];
1143                         *start += 1;
1144                 }
1145         }
1146
1147         /* Back to your regularly scheduled programming */
1148
1149  done:
1150 #ifdef DEBUG_NSS
1151         fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1152                 user, nss_err_str(ret), ret);
1153 #endif
1154
1155 #if HAVE_PTHREAD
1156         pthread_mutex_unlock(&winbind_nss_mutex);
1157 #endif
1158
1159         return ret;
1160 }
1161
1162
1163 /* return a list of group SIDs for a user SID */
1164 NSS_STATUS
1165 _nss_winbind_getusersids(const char *user_sid, char **group_sids,
1166                          int *num_groups,
1167                          char *buffer, size_t buf_size, int *errnop)
1168 {
1169         NSS_STATUS ret;
1170         struct winbindd_request request;
1171         struct winbindd_response response;
1172
1173 #ifdef DEBUG_NSS
1174         fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid);
1175 #endif
1176
1177 #if HAVE_PTHREAD
1178         pthread_mutex_lock(&winbind_nss_mutex);
1179 #endif
1180
1181         ZERO_STRUCT(request);
1182         ZERO_STRUCT(response);
1183
1184         strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1);
1185         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1186
1187         ret = winbindd_request_response(NULL, WINBINDD_GETUSERSIDS,
1188                                         &request, &response);
1189
1190         if (ret != NSS_STATUS_SUCCESS) {
1191                 goto done;
1192         }
1193
1194         if (buf_size < response.length - sizeof(response)) {
1195                 ret = NSS_STATUS_TRYAGAIN;
1196                 errno = *errnop = ERANGE;
1197                 goto done;
1198         }
1199
1200         *num_groups = response.data.num_entries;
1201         *group_sids = buffer;
1202         memcpy(buffer, response.extra_data.data, response.length - sizeof(response));
1203         errno = *errnop = 0;
1204
1205  done:
1206         winbindd_free_response(&response);
1207
1208 #if HAVE_PTHREAD
1209         pthread_mutex_unlock(&winbind_nss_mutex);
1210 #endif
1211
1212         return ret;
1213 }
1214
1215
1216 /* map a user or group name to a SID string */
1217 NSS_STATUS
1218 _nss_winbind_nametosid(const char *name, char **sid, char *buffer,
1219                        size_t buflen, int *errnop)
1220 {
1221         NSS_STATUS ret;
1222         struct winbindd_response response;
1223         struct winbindd_request request;
1224
1225 #ifdef DEBUG_NSS
1226         fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name);
1227 #endif
1228
1229 #if HAVE_PTHREAD
1230         pthread_mutex_lock(&winbind_nss_mutex);
1231 #endif
1232
1233         ZERO_STRUCT(response);
1234         ZERO_STRUCT(request);
1235
1236         strncpy(request.data.name.name, name,
1237                 sizeof(request.data.name.name) - 1);
1238         request.data.name.name[sizeof(request.data.name.name) - 1] = '\0';
1239
1240         ret = winbindd_request_response(NULL, WINBINDD_LOOKUPNAME,
1241                                         &request, &response);
1242         if (ret != NSS_STATUS_SUCCESS) {
1243                 *errnop = errno = EINVAL;
1244                 goto failed;
1245         }
1246
1247         if (buflen < strlen(response.data.sid.sid)+1) {
1248                 ret = NSS_STATUS_TRYAGAIN;
1249                 *errnop = errno = ERANGE;
1250                 goto failed;
1251         }
1252
1253         *errnop = errno = 0;
1254         *sid = buffer;
1255         strcpy(*sid, response.data.sid.sid);
1256
1257 failed:
1258         winbindd_free_response(&response);
1259
1260 #if HAVE_PTHREAD
1261         pthread_mutex_unlock(&winbind_nss_mutex);
1262 #endif
1263
1264         return ret;
1265 }
1266
1267 /* map a sid string to a user or group name */
1268 NSS_STATUS
1269 _nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
1270                        size_t buflen, int *errnop)
1271 {
1272         NSS_STATUS ret;
1273         struct winbindd_response response;
1274         struct winbindd_request request;
1275         static char sep_char;
1276         unsigned needed;
1277
1278 #ifdef DEBUG_NSS
1279         fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid);
1280 #endif
1281
1282 #if HAVE_PTHREAD
1283         pthread_mutex_lock(&winbind_nss_mutex);
1284 #endif
1285
1286         ZERO_STRUCT(response);
1287         ZERO_STRUCT(request);
1288
1289         /* we need to fetch the separator first time through */
1290         if (!sep_char) {
1291                 ret = winbindd_request_response(NULL, WINBINDD_INFO,
1292                                                 &request, &response);
1293                 if (ret != NSS_STATUS_SUCCESS) {
1294                         *errnop = errno = EINVAL;
1295                         goto failed;
1296                 }
1297
1298                 sep_char = response.data.info.winbind_separator;
1299                 winbindd_free_response(&response);
1300         }
1301
1302
1303         strncpy(request.data.sid, sid,
1304                 sizeof(request.data.sid) - 1);
1305         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1306
1307         ret = winbindd_request_response(NULL, WINBINDD_LOOKUPSID,
1308                                         &request, &response);
1309         if (ret != NSS_STATUS_SUCCESS) {
1310                 *errnop = errno = EINVAL;
1311                 goto failed;
1312         }
1313
1314         needed =
1315                 strlen(response.data.name.dom_name) +
1316                 strlen(response.data.name.name) + 2;
1317
1318         if (buflen < needed) {
1319                 ret = NSS_STATUS_TRYAGAIN;
1320                 *errnop = errno = ERANGE;
1321                 goto failed;
1322         }
1323
1324         snprintf(buffer, needed, "%s%c%s",
1325                  response.data.name.dom_name,
1326                  sep_char,
1327                  response.data.name.name);
1328
1329         *name = buffer;
1330         *errnop = errno = 0;
1331
1332 failed:
1333         winbindd_free_response(&response);
1334
1335 #if HAVE_PTHREAD
1336         pthread_mutex_unlock(&winbind_nss_mutex);
1337 #endif
1338
1339         return ret;
1340 }
1341
1342 /* map a sid to a uid */
1343 NSS_STATUS
1344 _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop)
1345 {
1346         NSS_STATUS ret;
1347         struct winbindd_response response;
1348         struct winbindd_request request;
1349
1350 #ifdef DEBUG_NSS
1351         fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid);
1352 #endif
1353
1354 #if HAVE_PTHREAD
1355         pthread_mutex_lock(&winbind_nss_mutex);
1356 #endif
1357
1358         ZERO_STRUCT(request);
1359         ZERO_STRUCT(response);
1360
1361         strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
1362         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1363
1364         ret = winbindd_request_response(NULL, WINBINDD_SID_TO_UID,
1365                                         &request, &response);
1366         if (ret != NSS_STATUS_SUCCESS) {
1367                 *errnop = errno = EINVAL;
1368                 goto failed;
1369         }
1370
1371         *uid = response.data.uid;
1372
1373 failed:
1374
1375 #if HAVE_PTHREAD
1376         pthread_mutex_unlock(&winbind_nss_mutex);
1377 #endif
1378
1379         return ret;
1380 }
1381
1382 /* map a sid to a gid */
1383 NSS_STATUS
1384 _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop)
1385 {
1386         NSS_STATUS ret;
1387         struct winbindd_response response;
1388         struct winbindd_request request;
1389
1390 #ifdef DEBUG_NSS
1391         fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid);
1392 #endif
1393
1394 #if HAVE_PTHREAD
1395         pthread_mutex_lock(&winbind_nss_mutex);
1396 #endif
1397
1398         ZERO_STRUCT(request);
1399         ZERO_STRUCT(response);
1400
1401         strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
1402         request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1403
1404         ret = winbindd_request_response(NULL, WINBINDD_SID_TO_GID,
1405                                         &request, &response);
1406         if (ret != NSS_STATUS_SUCCESS) {
1407                 *errnop = errno = EINVAL;
1408                 goto failed;
1409         }
1410
1411         *gid = response.data.gid;
1412
1413 failed:
1414
1415 #if HAVE_PTHREAD
1416         pthread_mutex_unlock(&winbind_nss_mutex);
1417 #endif
1418
1419         return ret;
1420 }
1421
1422 /* map a uid to a SID string */
1423 NSS_STATUS
1424 _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
1425                       size_t buflen, int *errnop)
1426 {
1427         NSS_STATUS ret;
1428         struct winbindd_response response;
1429         struct winbindd_request request;
1430
1431 #ifdef DEBUG_NSS
1432         fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid);
1433 #endif
1434
1435 #if HAVE_PTHREAD
1436         pthread_mutex_lock(&winbind_nss_mutex);
1437 #endif
1438
1439         ZERO_STRUCT(response);
1440         ZERO_STRUCT(request);
1441
1442         request.data.uid = uid;
1443
1444         ret = winbindd_request_response(NULL, WINBINDD_UID_TO_SID,
1445                                         &request, &response);
1446         if (ret != NSS_STATUS_SUCCESS) {
1447                 *errnop = errno = EINVAL;
1448                 goto failed;
1449         }
1450
1451         if (buflen < strlen(response.data.sid.sid)+1) {
1452                 ret = NSS_STATUS_TRYAGAIN;
1453                 *errnop = errno = ERANGE;
1454                 goto failed;
1455         }
1456
1457         *errnop = errno = 0;
1458         *sid = buffer;
1459         strcpy(*sid, response.data.sid.sid);
1460
1461 failed:
1462         winbindd_free_response(&response);
1463
1464 #if HAVE_PTHREAD
1465         pthread_mutex_unlock(&winbind_nss_mutex);
1466 #endif
1467
1468         return ret;
1469 }
1470
1471 /* map a gid to a SID string */
1472 NSS_STATUS
1473 _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
1474                       size_t buflen, int *errnop)
1475 {
1476         NSS_STATUS ret;
1477         struct winbindd_response response;
1478         struct winbindd_request request;
1479
1480 #ifdef DEBUG_NSS
1481         fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid);
1482 #endif
1483
1484 #if HAVE_PTHREAD
1485         pthread_mutex_lock(&winbind_nss_mutex);
1486 #endif
1487
1488         ZERO_STRUCT(response);
1489         ZERO_STRUCT(request);
1490
1491         request.data.gid = gid;
1492
1493         ret = winbindd_request_response(NULL, WINBINDD_GID_TO_SID,
1494                                         &request, &response);
1495         if (ret != NSS_STATUS_SUCCESS) {
1496                 *errnop = errno = EINVAL;
1497                 goto failed;
1498         }
1499
1500         if (buflen < strlen(response.data.sid.sid)+1) {
1501                 ret = NSS_STATUS_TRYAGAIN;
1502                 *errnop = errno = ERANGE;
1503                 goto failed;
1504         }
1505
1506         *errnop = errno = 0;
1507         *sid = buffer;
1508         strcpy(*sid, response.data.sid.sid);
1509
1510 failed:
1511         winbindd_free_response(&response);
1512
1513 #if HAVE_PTHREAD
1514         pthread_mutex_unlock(&winbind_nss_mutex);
1515 #endif
1516
1517         return ret;
1518 }