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