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