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