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