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