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