first public release of samba4 code
[ira/wip.git] / source / 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_client.h"
25
26 #ifdef HAVE_NS_API_H
27 #undef VOLATILE
28
29 #include <ns_daemon.h>
30 #endif
31
32 #define MAX_GETPWENT_USERS 250
33 #define MAX_GETGRENT_USERS 250
34
35 /* Prototypes from wb_common.c */
36
37 extern int winbindd_fd;
38
39
40 #ifdef HAVE_NS_API_H
41 /* IRIX version */
42
43 static int send_next_request(nsd_file_t *, struct winbindd_request *);
44 static int do_list(int state, nsd_file_t *rq);
45
46 static nsd_file_t *current_rq = NULL;
47 static int current_winbind_xid = 0;
48 static int next_winbind_xid = 0;
49
50 typedef struct winbind_xid {
51         int                     xid;
52         nsd_file_t              *rq;
53         struct winbindd_request *request;
54         struct winbind_xid      *next;
55 } winbind_xid_t;
56
57 static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
58
59 static int
60 winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
61 {
62         winbind_xid_t *new;
63
64         nsd_logprintf(NSD_LOG_LOW,
65                 "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
66                 xid, rq, request);
67         new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
68         if (!new) {
69                 nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
70                 return NSD_ERROR;
71         }
72
73         new->xid = xid;
74         new->rq = rq;
75         new->request = request;
76         new->next = winbind_xids;
77         winbind_xids = new;
78
79         return NSD_CONTINUE;
80 }
81
82 /*
83 ** This routine will look down the xid list and return the request
84 ** associated with an xid.  We remove the record if it is found.
85 */
86 nsd_file_t *
87 winbind_xid_lookup(int xid, struct winbindd_request **requestp)
88 {
89         winbind_xid_t **last, *dx;
90         nsd_file_t *result=0;
91
92         for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
93             last = &dx->next, dx = dx->next);
94         if (dx) {
95                 *last = dx->next;
96                 result = dx->rq;
97                 *requestp = dx->request;
98                 SAFE_FREE(dx);
99         }
100         nsd_logprintf(NSD_LOG_LOW,
101                 "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
102                 xid, result, dx->request);
103
104         return result;
105 }
106
107 static int
108 winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
109 {
110         nsd_file_t *rq;
111         struct winbindd_request *request;
112
113         nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
114         rq = to->t_file;
115         *rqp = rq;
116         nsd_timeout_remove(rq);
117         request = to->t_clientdata;
118         return(send_next_request(rq, request));
119 }
120
121 static void
122 dequeue_request()
123 {
124         nsd_file_t *rq;
125         struct winbindd_request *request;
126
127         /*
128          * Check for queued requests
129          */
130         if (winbind_xids) {
131             nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
132                         current_winbind_xid);
133             rq = winbind_xid_lookup(current_winbind_xid++, &request);
134             /* cause a timeout on the queued request so we can send it */
135             nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
136         }
137 }
138
139 static int
140 do_request(nsd_file_t *rq, struct winbindd_request *request)
141 {
142         if (winbind_xids == NULL) {
143                 /*
144                  * No outstanding requests.
145                  * Send off the request to winbindd
146                  */
147                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
148                 return(send_next_request(rq, request));
149         } else {
150                 /*
151                  * Just queue it up for now - previous callout or timout
152                  * will start it up
153                  */
154                 nsd_logprintf(NSD_LOG_MIN,
155                         "lookup (winbind): queue request xid = %d\n",
156                         next_winbind_xid);
157                 return(winbind_xid_new(next_winbind_xid++, rq, request));
158         }
159 }
160
161 static int 
162 winbind_callback(nsd_file_t **rqp, int fd)
163 {
164         struct winbindd_response response;
165         struct winbindd_pw *pw = &response.data.pw;
166         struct winbindd_gr *gr = &response.data.gr;
167         nsd_file_t *rq;
168         NSS_STATUS status;
169         fstring result;
170         char *members;
171         int i, maxlen;
172
173         dequeue_request();
174
175         nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
176
177         rq = current_rq;
178         *rqp = rq;
179
180         nsd_timeout_remove(rq);
181         nsd_callback_remove(fd);
182
183         ZERO_STRUCT(response);
184         status = winbindd_get_response(&response);
185
186         if (status != NSS_STATUS_SUCCESS) {
187                 /* free any extra data area in response structure */
188                 free_response(&response);
189                 nsd_logprintf(NSD_LOG_MIN, 
190                         "callback (winbind) returning not found, status = %d\n",
191                         status);
192                 rq->f_status = NS_NOTFOUND;
193                 return NSD_NEXT;
194         }
195
196         maxlen = sizeof(result) - 1;
197
198         switch ((int)rq->f_cmd_data) {
199             case WINBINDD_WINS_BYNAME:
200             case WINBINDD_WINS_BYIP:
201                 snprintf(result,maxlen,"%s\n",response.data.winsresp);
202                 break;
203             case WINBINDD_GETPWUID:
204             case WINBINDD_GETPWNAM:
205                 snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s\n",
206                         pw->pw_name,
207                         pw->pw_passwd,
208                         pw->pw_uid,
209                         pw->pw_gid,
210                         pw->pw_gecos,
211                         pw->pw_dir,
212                         pw->pw_shell);
213                 break;
214             case WINBINDD_GETGRNAM:
215             case WINBINDD_GETGRGID:
216                 if (gr->num_gr_mem && response.extra_data)
217                         members = response.extra_data;
218                 else
219                         members = "";
220                 snprintf(result,maxlen,"%s:%s:%d:%s\n",
221                         gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
222                 break;
223             case WINBINDD_SETGRENT:
224             case WINBINDD_SETPWENT:
225                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - SETPWENT/SETGRENT\n");
226                 free_response(&response);
227                 return(do_list(1,rq));
228             case WINBINDD_GETGRENT:
229             case WINBINDD_GETGRLST:
230                 nsd_logprintf(NSD_LOG_MIN, 
231                         "callback (winbind) - %d GETGRENT responses\n",
232                         response.data.num_entries);
233                 if (response.data.num_entries) {
234                     gr = (struct winbindd_gr *)response.extra_data;
235                     if (! gr ) {
236                         nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
237                         free_response(&response);
238                         return NSD_ERROR;
239                     }
240                     members = (char *)response.extra_data + 
241                                 (response.data.num_entries * sizeof(struct winbindd_gr));
242                     for (i = 0; i < response.data.num_entries; i++) {
243                         snprintf(result,maxlen,"%s:%s:%d:%s\n",
244                                 gr->gr_name, gr->gr_passwd, gr->gr_gid, 
245                                 &members[gr->gr_mem_ofs]);
246                         nsd_logprintf(NSD_LOG_MIN, "     GETGRENT %s\n",result);
247                         nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
248                         gr++;
249                     }
250                 }
251                 i = response.data.num_entries;
252                 free_response(&response);
253                 if (i < MAX_GETPWENT_USERS)
254                     return(do_list(2,rq));
255                 else
256                     return(do_list(1,rq));
257             case WINBINDD_GETPWENT:
258                 nsd_logprintf(NSD_LOG_MIN, 
259                         "callback (winbind) - %d GETPWENT responses\n",
260                         response.data.num_entries);
261                 if (response.data.num_entries) {
262                     pw = (struct winbindd_pw *)response.extra_data;
263                     if (! pw ) {
264                         nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
265                         free_response(&response);
266                         return NSD_ERROR;
267                     }
268                     for (i = 0; i < response.data.num_entries; i++) {
269                         snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s",
270                                 pw->pw_name,
271                                 pw->pw_passwd,
272                                 pw->pw_uid,
273                                 pw->pw_gid,
274                                 pw->pw_gecos,
275                                 pw->pw_dir,
276                                 pw->pw_shell);
277                         nsd_logprintf(NSD_LOG_MIN, "     GETPWENT %s\n",result);
278                         nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
279                         pw++;
280                     }
281                 }
282                 i = response.data.num_entries;
283                 free_response(&response);
284                 if (i < MAX_GETPWENT_USERS)
285                     return(do_list(2,rq));
286                 else
287                     return(do_list(1,rq));
288             case WINBINDD_ENDGRENT:
289             case WINBINDD_ENDPWENT:
290                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - ENDPWENT/ENDGRENT\n");
291                 nsd_append_element(rq,NS_SUCCESS,"\n",1);
292                 free_response(&response);
293                 return NSD_NEXT;
294             default:
295                 free_response(&response);
296                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - no valid command\n");
297                 return NSD_NEXT;
298         }
299         nsd_logprintf(NSD_LOG_MIN, "callback (winbind) %s\n", result);
300         /* free any extra data area in response structure */
301         free_response(&response);
302         nsd_set_result(rq,NS_SUCCESS,result,strlen(result),VOLATILE);
303         return NSD_OK;
304 }
305
306 static int 
307 winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
308 {
309         nsd_file_t *rq;
310
311         dequeue_request();
312
313         nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
314
315         rq = to->t_file;
316         *rqp = rq;
317
318         /* Remove the callback and timeout */
319         nsd_callback_remove(winbindd_fd);
320         nsd_timeout_remove(rq);
321
322         rq->f_status = NS_NOTFOUND;
323         return NSD_NEXT;
324 }
325
326 static int
327 send_next_request(nsd_file_t *rq, struct winbindd_request *request)
328 {
329         NSS_STATUS status;
330         long timeout;
331
332         timeout = 1000;
333
334         nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) %d to = %d\n",
335                         rq->f_cmd_data, timeout);
336         status = winbindd_send_request((int)rq->f_cmd_data,request);
337         SAFE_FREE(request);
338
339         if (status != NSS_STATUS_SUCCESS) {
340                 nsd_logprintf(NSD_LOG_MIN, 
341                         "send_next_request (winbind) error status = %d\n",status);
342                 rq->f_status = status;
343                 return NSD_NEXT;
344         }
345
346         current_rq = rq;
347
348         /*
349          * Set up callback and timeouts
350          */
351         nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",winbindd_fd);
352         nsd_callback_new(winbindd_fd,winbind_callback,NSD_READ);
353         nsd_timeout_new(rq,timeout,winbind_timeout,(void *)0);
354         return NSD_CONTINUE;
355 }
356
357 int init(void)
358 {
359         nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
360         return(NSD_OK);
361 }
362
363 int lookup(nsd_file_t *rq)
364 {
365         char *map;
366         char *key;
367         struct winbindd_request *request;
368
369         nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
370         if (! rq)
371                 return NSD_ERROR;
372
373         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
374         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
375         if (! map || ! key) {
376                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
377                 rq->f_status = NS_BADREQ;
378                 return NSD_ERROR;
379         }
380
381         nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
382
383         request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
384         if (! request) {
385                 nsd_logprintf(NSD_LOG_RESOURCE,
386                         "lookup (winbind): failed malloc\n");
387                 return NSD_ERROR;
388         }
389
390         if (strcasecmp(map,"passwd.byuid") == 0) {
391             request->data.uid = atoi(key);
392             rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
393         } else if (strcasecmp(map,"passwd.byname") == 0) {
394             strncpy(request->data.username, key, 
395                 sizeof(request->data.username) - 1);
396             request->data.username[sizeof(request->data.username) - 1] = '\0';
397             rq->f_cmd_data = (void *)WINBINDD_GETPWNAM; 
398         } else if (strcasecmp(map,"group.byname") == 0) {
399             strncpy(request->data.groupname, key, 
400                 sizeof(request->data.groupname) - 1);
401             request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
402             rq->f_cmd_data = (void *)WINBINDD_GETGRNAM; 
403         } else if (strcasecmp(map,"group.bygid") == 0) {
404             request->data.gid = atoi(key);
405             rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
406         } else if (strcasecmp(map,"hosts.byname") == 0) {
407             strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
408             request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
409             rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
410         } else if (strcasecmp(map,"hosts.byaddr") == 0) {
411             strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
412             request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
413             rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
414         } else {
415                 /*
416                  * Don't understand this map - just return not found
417                  */
418                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
419                 SAFE_FREE(request);
420                 rq->f_status = NS_NOTFOUND;
421                 return NSD_NEXT;
422         }
423
424         return(do_request(rq, request));
425 }
426
427 int list(nsd_file_t *rq)
428 {
429         char *map;
430
431         nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
432         if (! rq)
433                 return NSD_ERROR;
434
435         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
436         if (! map ) {
437                 nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
438                 rq->f_status = NS_BADREQ;
439                 return NSD_ERROR;
440         }
441
442         nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
443
444         return (do_list(0,rq));
445 }
446
447 static int
448 do_list(int state, nsd_file_t *rq)
449 {
450         char *map;
451         struct winbindd_request *request;
452
453         nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
454
455         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
456         request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
457         if (! request) {
458                 nsd_logprintf(NSD_LOG_RESOURCE,
459                         "do_list (winbind): failed malloc\n");
460                 return NSD_ERROR;
461         }
462
463         if (strcasecmp(map,"passwd.byname") == 0) {
464             switch (state) {
465                 case 0:
466                     rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
467                     break;
468                 case 1:
469                     request->data.num_entries = MAX_GETPWENT_USERS;
470                     rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
471                     break;
472                 case 2:
473                     rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
474                     break;
475                 default:
476                     nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
477                     SAFE_FREE(request);
478                     rq->f_status = NS_NOTFOUND;
479                     return NSD_NEXT;
480             }
481         } else if (strcasecmp(map,"group.byname") == 0) {
482             switch (state) {
483                 case 0:
484                     rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
485                     break;
486                 case 1:
487                     request->data.num_entries = MAX_GETGRENT_USERS;
488                     rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
489                     break;
490                 case 2:
491                     rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
492                     break;
493                 default:
494                     nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
495                     SAFE_FREE(request);
496                     rq->f_status = NS_NOTFOUND;
497                     return NSD_NEXT;
498             }
499         } else {
500                 /*
501                  * Don't understand this map - just return not found
502                  */
503                 nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
504                 SAFE_FREE(request);
505                 rq->f_status = NS_NOTFOUND;
506                 return NSD_NEXT;
507         }
508
509         return(do_request(rq, request));
510 }
511
512 #else
513
514 /* Allocate some space from the nss static buffer.  The buffer and buflen
515    are the pointers passed in by the C library to the _nss_ntdom_*
516    functions. */
517
518 static char *get_static(char **buffer, int *buflen, int len)
519 {
520         char *result;
521
522         /* Error check.  We return false if things aren't set up right, or
523            there isn't enough buffer space left. */
524         
525         if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
526                 return NULL;
527         }
528
529         /* Return an index into the static buffer */
530
531         result = *buffer;
532         *buffer += len;
533         *buflen -= len;
534
535         return result;
536 }
537
538 /* I've copied the strtok() replacement function next_token() from
539    lib/util_str.c as I really don't want to have to link in any other
540    objects if I can possibly avoid it. */
541
542 BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
543 {
544         char *s;
545         BOOL quoted;
546         size_t len=1;
547
548         if (!ptr) return(False);
549
550         s = *ptr;
551
552         /* default to simple separators */
553         if (!sep) sep = " \t\n\r";
554
555         /* find the first non sep char */
556         while (*s && strchr(sep,*s)) s++;
557         
558         /* nothing left? */
559         if (! *s) return(False);
560         
561         /* copy over the token */
562         for (quoted = False; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) {
563                 if (*s == '\"') {
564                         quoted = !quoted;
565                 } else {
566                         len++;
567                         *buff++ = *s;
568                 }
569         }
570         
571         *ptr = (*s) ? s+1 : s;  
572         *buff = 0;
573         
574         return(True);
575 }
576
577
578 /* Fill a pwent structure from a winbindd_response structure.  We use
579    the static data passed to us by libc to put strings and stuff in.
580    Return NSS_STATUS_TRYAGAIN if we run out of memory. */
581
582 static NSS_STATUS fill_pwent(struct passwd *result,
583                                   struct winbindd_pw *pw,
584                                   char **buffer, size_t *buflen)
585 {
586         /* User name */
587
588         if ((result->pw_name = 
589              get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
590
591                 /* Out of memory */
592
593                 return NSS_STATUS_TRYAGAIN;
594         }
595
596         strcpy(result->pw_name, pw->pw_name);
597
598         /* Password */
599
600         if ((result->pw_passwd = 
601              get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
602
603                 /* Out of memory */
604
605                 return NSS_STATUS_TRYAGAIN;
606         }
607
608         strcpy(result->pw_passwd, pw->pw_passwd);
609         
610         /* [ug]id */
611
612         result->pw_uid = pw->pw_uid;
613         result->pw_gid = pw->pw_gid;
614
615         /* GECOS */
616
617         if ((result->pw_gecos = 
618              get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
619
620                 /* Out of memory */
621
622                 return NSS_STATUS_TRYAGAIN;
623         }
624
625         strcpy(result->pw_gecos, pw->pw_gecos);
626         
627         /* Home directory */
628         
629         if ((result->pw_dir = 
630              get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
631
632                 /* Out of memory */
633
634                 return NSS_STATUS_TRYAGAIN;
635         }
636
637         strcpy(result->pw_dir, pw->pw_dir);
638
639         /* Logon shell */
640         
641         if ((result->pw_shell = 
642              get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
643                 
644                 /* Out of memory */
645
646                 return NSS_STATUS_TRYAGAIN;
647         }
648
649         strcpy(result->pw_shell, pw->pw_shell);
650
651         /* The struct passwd for Solaris has some extra fields which must
652            be initialised or nscd crashes. */
653
654 #if HAVE_PASSWD_PW_COMMENT
655         result->pw_comment = "";
656 #endif
657
658 #if HAVE_PASSWD_PW_AGE
659         result->pw_age = "";
660 #endif
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 NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
670                       char *gr_mem, char **buffer, size_t *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 = (unsigned long)(*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 static NSS_STATUS
1046 winbind_getgrent(enum winbindd_cmd cmd,
1047                  struct group *result,
1048                  char *buffer, size_t buflen, int *errnop)
1049 {
1050         NSS_STATUS ret;
1051         static struct winbindd_request request;
1052         static int called_again;
1053         
1054
1055 #ifdef DEBUG_NSS
1056         fprintf(stderr, "[%5d]: getgrent\n", getpid());
1057 #endif
1058
1059         /* Return an entry from the cache if we have one, or if we are
1060            called again because we exceeded our static buffer.  */
1061
1062         if ((ndx_gr_cache < num_gr_cache) || called_again) {
1063                 goto return_result;
1064         }
1065
1066         /* Else call winbindd to get a bunch of entries */
1067         
1068         if (num_gr_cache > 0) {
1069                 free_response(&getgrent_response);
1070         }
1071
1072         ZERO_STRUCT(request);
1073         ZERO_STRUCT(getgrent_response);
1074
1075         request.data.num_entries = MAX_GETGRENT_USERS;
1076
1077         ret = winbindd_request(cmd, &request, 
1078                                &getgrent_response);
1079
1080         if (ret == NSS_STATUS_SUCCESS) {
1081                 struct winbindd_gr *gr_cache;
1082                 int mem_ofs;
1083
1084                 /* Fill cache */
1085
1086                 ndx_gr_cache = 0;
1087                 num_gr_cache = getgrent_response.data.num_entries;
1088
1089                 /* Return a result */
1090
1091         return_result:
1092
1093                 gr_cache = getgrent_response.extra_data;
1094
1095                 /* Check data is valid */
1096
1097                 if (gr_cache == NULL) {
1098                         return NSS_STATUS_NOTFOUND;
1099                 }
1100
1101                 /* Fill group membership.  The offset into the extra data
1102                    for the group membership is the reported offset plus the
1103                    size of all the winbindd_gr records returned. */
1104
1105                 mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
1106                         num_gr_cache * sizeof(struct winbindd_gr);
1107
1108                 ret = fill_grent(result, &gr_cache[ndx_gr_cache],
1109                                  ((char *)getgrent_response.extra_data)+mem_ofs,
1110                                  &buffer, &buflen);
1111                 
1112                 /* Out of memory - try again */
1113
1114                 if (ret == NSS_STATUS_TRYAGAIN) {
1115                         called_again = True;
1116                         *errnop = errno = ERANGE;
1117                         return ret;
1118                 }
1119
1120                 *errnop = 0;
1121                 called_again = False;
1122                 ndx_gr_cache++;
1123
1124                 /* If we've finished with this lot of results free cache */
1125
1126                 if (ndx_gr_cache == num_gr_cache) {
1127                         ndx_gr_cache = num_gr_cache = 0;
1128                         free_response(&getgrent_response);
1129                 }
1130         }
1131
1132         return ret;
1133 }
1134
1135
1136 NSS_STATUS
1137 _nss_winbind_getgrent_r(struct group *result,
1138                         char *buffer, size_t buflen, int *errnop)
1139 {
1140         return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
1141 }
1142
1143 NSS_STATUS
1144 _nss_winbind_getgrlst_r(struct group *result,
1145                         char *buffer, size_t buflen, int *errnop)
1146 {
1147         return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
1148 }
1149
1150 /* Return group struct from group name */
1151
1152 NSS_STATUS
1153 _nss_winbind_getgrnam_r(const char *name,
1154                         struct group *result, char *buffer,
1155                         size_t buflen, int *errnop)
1156 {
1157         NSS_STATUS ret;
1158         static struct winbindd_response response;
1159         struct winbindd_request request;
1160         static int keep_response;
1161         
1162 #ifdef DEBUG_NSS
1163         fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
1164 #endif
1165
1166         /* If our static buffer needs to be expanded we are called again */
1167         
1168         if (!keep_response) {
1169
1170                 /* Call for the first time */
1171
1172                 ZERO_STRUCT(request);
1173                 ZERO_STRUCT(response);
1174
1175                 strncpy(request.data.groupname, name, 
1176                         sizeof(request.data.groupname));
1177                 request.data.groupname
1178                         [sizeof(request.data.groupname) - 1] = '\0';
1179
1180                 ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
1181
1182                 if (ret == NSS_STATUS_SUCCESS) {
1183                         ret = fill_grent(result, &response.data.gr, 
1184                                          response.extra_data,
1185                                          &buffer, &buflen);
1186
1187                         if (ret == NSS_STATUS_TRYAGAIN) {
1188                                 keep_response = True;
1189                                 *errnop = errno = ERANGE;
1190                                 return ret;
1191                         }
1192                 }
1193
1194         } else {
1195                 
1196                 /* We've been called again */
1197                 
1198                 ret = fill_grent(result, &response.data.gr, 
1199                                  response.extra_data, &buffer, &buflen);
1200                 
1201                 if (ret == NSS_STATUS_TRYAGAIN) {
1202                         keep_response = True;
1203                         *errnop = errno = ERANGE;
1204                         return ret;
1205                 }
1206
1207                 keep_response = False;
1208                 *errnop = 0;
1209         }
1210
1211         free_response(&response);
1212         return ret;
1213 }
1214
1215 /* Return group struct from gid */
1216
1217 NSS_STATUS
1218 _nss_winbind_getgrgid_r(gid_t gid,
1219                         struct group *result, char *buffer,
1220                         size_t buflen, int *errnop)
1221 {
1222         NSS_STATUS ret;
1223         static struct winbindd_response response;
1224         struct winbindd_request request;
1225         static int keep_response;
1226
1227 #ifdef DEBUG_NSS
1228         fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
1229 #endif
1230
1231         /* If our static buffer needs to be expanded we are called again */
1232
1233         if (!keep_response) {
1234
1235                 /* Call for the first time */
1236
1237                 ZERO_STRUCT(request);
1238                 ZERO_STRUCT(response);
1239
1240                 request.data.gid = gid;
1241
1242                 ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
1243
1244                 if (ret == NSS_STATUS_SUCCESS) {
1245
1246                         ret = fill_grent(result, &response.data.gr, 
1247                                          response.extra_data, 
1248                                          &buffer, &buflen);
1249
1250                         if (ret == NSS_STATUS_TRYAGAIN) {
1251                                 keep_response = True;
1252                                 *errnop = errno = ERANGE;
1253                                 return ret;
1254                         }
1255                 }
1256
1257         } else {
1258
1259                 /* We've been called again */
1260
1261                 ret = fill_grent(result, &response.data.gr, 
1262                                  response.extra_data, &buffer, &buflen);
1263
1264                 if (ret == NSS_STATUS_TRYAGAIN) {
1265                         keep_response = True;
1266                         *errnop = errno = ERANGE;
1267                         return ret;
1268                 }
1269
1270                 keep_response = False;
1271                 *errnop = 0;
1272         }
1273
1274         free_response(&response);
1275         return ret;
1276 }
1277
1278 /* Initialise supplementary groups */
1279
1280 NSS_STATUS
1281 _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
1282                             long int *size, gid_t **groups, long int limit,
1283                             int *errnop)
1284 {
1285         NSS_STATUS ret;
1286         struct winbindd_request request;
1287         struct winbindd_response response;
1288         int i;
1289
1290 #ifdef DEBUG_NSS
1291         fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
1292                 user, group);
1293 #endif
1294
1295         ZERO_STRUCT(request);
1296         ZERO_STRUCT(response);
1297
1298         strncpy(request.data.username, user,
1299                 sizeof(request.data.username) - 1);
1300
1301         ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
1302
1303         if (ret == NSS_STATUS_SUCCESS) {
1304                 int num_gids = response.data.num_entries;
1305                 gid_t *gid_list = (gid_t *)response.extra_data;
1306
1307                 /* Copy group list to client */
1308
1309                 for (i = 0; i < num_gids; i++) {
1310
1311                         /* Skip primary group */
1312
1313                         if (gid_list[i] == group) continue;
1314
1315                         /* Add to buffer */
1316
1317                         if (*start == *size && limit <= 0) {
1318                                 (*groups) = realloc(
1319                                         (*groups), (2 * (*size) + 1) * sizeof(**groups));
1320                                 if (! *groups) goto done;
1321                                 *size = 2 * (*size) + 1;
1322                         }
1323
1324                         if (*start == *size) goto done;
1325
1326                         (*groups)[*start] = gid_list[i];
1327                         *start += 1;
1328
1329                         /* Filled buffer? */
1330
1331                         if (*start == limit) goto done;
1332                 }
1333         }
1334         
1335         /* Back to your regularly scheduled programming */
1336
1337  done:
1338         return ret;
1339 }
1340
1341 #endif