r21935: Revert obviously not sufficiently tested code -- sorry for the pain. I am...
[tprouty/samba.git] / source / nsswitch / winbind_nss_irix.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Windows NT Domain nsswitch module
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) James Peach 2006
8    
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Library General Public
11    License as published by the Free Software Foundation; either
12    version 2 of the License, or (at your option) any later version.
13    
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Library General Public License for more details.
18    
19    You should have received a copy of the GNU Library General Public
20    License along with this library; if not, write to the
21    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22    Boston, MA  02111-1307, USA.   
23 */
24
25 #include "winbind_client.h"
26
27 #ifndef PRINTF_ATTRIBUTE
28 #define PRINTF_ATTRIBUTE(m, n)
29 #endif
30
31 #ifndef HAVE_ASPRINTF_DECL
32 /*PRINTFLIKE2 */
33 int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3);
34 #endif
35
36 #ifdef HAVE_NS_API_H
37 #undef VOLATILE
38 #undef STATIC
39 #undef DYNAMIC
40 #include <ns_daemon.h>
41 #endif
42
43 /* Maximum number of users to pass back over the unix domain socket
44    per call. This is not a static limit on the total number of users 
45    or groups returned in total. */
46
47 #define MAX_GETPWENT_USERS 250
48 #define MAX_GETGRENT_USERS 250
49
50 /* Prototypes from wb_common.c */
51
52 extern int winbindd_fd;
53
54 #ifdef HAVE_NS_API_H
55
56 /* IRIX version */
57
58 static int send_next_request(nsd_file_t *, struct winbindd_request *);
59 static int do_list(int state, nsd_file_t *rq);
60
61 static nsd_file_t *current_rq = NULL;
62 static int current_winbind_xid = 0;
63 static int next_winbind_xid = 0;
64
65 typedef struct winbind_xid {
66         int                     xid;
67         nsd_file_t              *rq;
68         struct winbindd_request *request;
69         struct winbind_xid      *next;
70 } winbind_xid_t;
71
72 static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
73
74 static int
75 winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
76 {
77         winbind_xid_t *new;
78
79         nsd_logprintf(NSD_LOG_LOW,
80                 "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
81                 xid, rq, request);
82         new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
83         if (!new) {
84                 nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
85                 return NSD_ERROR;
86         }
87
88         new->xid = xid;
89         new->rq = rq;
90         new->request = request;
91         new->next = winbind_xids;
92         winbind_xids = new;
93
94         return NSD_CONTINUE;
95 }
96
97 /*
98 ** This routine will look down the xid list and return the request
99 ** associated with an xid.  We remove the record if it is found.
100 */
101 nsd_file_t *
102 winbind_xid_lookup(int xid, struct winbindd_request **requestp)
103 {
104         winbind_xid_t **last, *dx;
105         nsd_file_t *result=0;
106
107         for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
108             last = &dx->next, dx = dx->next);
109         if (dx) {
110                 *last = dx->next;
111                 result = dx->rq;
112                 *requestp = dx->request;
113                 SAFE_FREE(dx);
114         }
115         nsd_logprintf(NSD_LOG_LOW,
116                 "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
117                 xid, result, dx->request);
118
119         return result;
120 }
121
122 static int
123 winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
124 {
125         nsd_file_t *rq;
126         struct winbindd_request *request;
127
128         nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
129         rq = to->t_file;
130         *rqp = rq;
131         nsd_timeout_remove(rq);
132         request = to->t_clientdata;
133         return(send_next_request(rq, request));
134 }
135
136 static void
137 dequeue_request(void)
138 {
139         nsd_file_t *rq;
140         struct winbindd_request *request;
141
142         /*
143          * Check for queued requests
144          */
145         if (winbind_xids) {
146             nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
147                         current_winbind_xid);
148             rq = winbind_xid_lookup(current_winbind_xid++, &request);
149             /* cause a timeout on the queued request so we can send it */
150             nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
151         }
152 }
153
154 static int
155 do_request(nsd_file_t *rq, struct winbindd_request *request)
156 {
157         if (winbind_xids == NULL) {
158                 /*
159                  * No outstanding requests.
160                  * Send off the request to winbindd
161                  */
162                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
163                 return(send_next_request(rq, request));
164         } else {
165                 /*
166                  * Just queue it up for now - previous callout or timout
167                  * will start it up
168                  */
169                 nsd_logprintf(NSD_LOG_MIN,
170                         "lookup (winbind): queue request xid = %d\n",
171                         next_winbind_xid);
172                 return(winbind_xid_new(next_winbind_xid++, rq, request));
173         }
174 }
175
176 static int 
177 winbind_callback(nsd_file_t **rqp, int fd)
178 {
179         struct winbindd_response response;
180         nsd_file_t *rq;
181         NSS_STATUS status;
182         char * result = NULL;
183         size_t rlen;
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
205                 switch (status) {
206                     case NSS_STATUS_UNAVAIL:
207                         rq->f_status = NS_UNAVAIL;
208                         break;
209                     case NSS_STATUS_TRYAGAIN:
210                         rq->f_status = NS_TRYAGAIN;
211                         break;
212                     case NSS_STATUS_NOTFOUND:
213                         /* FALLTHRU */
214                     default:
215                         rq->f_status = NS_NOTFOUND;
216                 }
217
218                 return NSD_NEXT;
219         }
220
221         switch ((int)rq->f_cmd_data) {
222             case WINBINDD_WINS_BYNAME:
223             case WINBINDD_WINS_BYIP:
224                 nsd_logprintf(NSD_LOG_MIN,
225                         "callback (winbind) WINS_BYNAME | WINS_BYIP\n");
226
227                 rlen = asprintf(&result, "%s\n", response.data.winsresp);
228                 if (rlen == 0 || result == NULL) {
229                         return NSD_ERROR;
230                 }
231                 
232                 free_response(&response);
233                 
234                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
235                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
236                 return NSD_OK;
237
238             case WINBINDD_GETPWUID:
239             case WINBINDD_GETPWNAM:
240             {
241                 struct winbindd_pw *pw = &response.data.pw;
242             
243                 nsd_logprintf(NSD_LOG_MIN,
244                         "callback (winbind) GETPWUID | GETPWUID\n");
245
246                 rlen = asprintf(&result,"%s:%s:%d:%d:%s:%s:%s\n",
247                                 pw->pw_name,
248                                 pw->pw_passwd,
249                                 pw->pw_uid,
250                                 pw->pw_gid,
251                                 pw->pw_gecos,
252                                 pw->pw_dir,
253                                 pw->pw_shell);
254                 if (rlen == 0 || result == NULL)
255                     return NSD_ERROR;
256             
257                 free_response(&response);
258             
259                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
260                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
261                 return NSD_OK;
262             }
263
264             case WINBINDD_GETGRNAM:
265             case WINBINDD_GETGRGID:
266             {
267                 const struct winbindd_gr *gr = &response.data.gr;
268                 const char * members;
269             
270                 nsd_logprintf(NSD_LOG_MIN,
271                         "callback (winbind) GETGRNAM | GETGRGID\n");
272
273                 if (gr->num_gr_mem && response.extra_data.data) {
274                         members = response.extra_data.data;
275                 } else {
276                         members = "";
277                 }
278             
279                 rlen = asprintf(&result, "%s:%s:%d:%s\n",
280                             gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
281                 if (rlen == 0 || result == NULL)
282                     return NSD_ERROR;
283             
284                 free_response(&response);
285             
286                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
287                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
288                 return NSD_OK;
289             }
290
291             case WINBINDD_SETGRENT:
292             case WINBINDD_SETPWENT:
293                 nsd_logprintf(NSD_LOG_MIN,
294                         "callback (winbind) SETGRENT | SETPWENT\n");
295                 free_response(&response);
296                 return(do_list(1,rq));
297
298             case WINBINDD_GETGRENT:
299             case WINBINDD_GETGRLST:
300             {
301                 int entries;
302             
303                 nsd_logprintf(NSD_LOG_MIN,
304                     "callback (winbind) GETGRENT | GETGRLIST %d responses\n",
305                     response.data.num_entries);
306             
307                 if (response.data.num_entries) {
308                     const struct winbindd_gr *gr = &response.data.gr;
309                     const char * members;
310                     fstring grp_name;
311                     int     i;
312             
313                     gr = (struct winbindd_gr *)response.extra_data.data;
314                     if (! gr ) {
315                         nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
316                         free_response(&response);
317                         return NSD_ERROR;
318                     }
319             
320                     members = (char *)response.extra_data.data +
321                         (response.data.num_entries * sizeof(struct winbindd_gr));
322             
323                     for (i = 0; i < response.data.num_entries; i++) {
324                         snprintf(grp_name, sizeof(grp_name) - 1, "%s:%s:%d:",
325                                     gr->gr_name, gr->gr_passwd, gr->gr_gid);
326             
327                         nsd_append_element(rq, NS_SUCCESS, result, rlen);
328                         nsd_append_result(rq, NS_SUCCESS,
329                                 &members[gr->gr_mem_ofs],
330                                 strlen(&members[gr->gr_mem_ofs]));
331             
332                         /* Don't log the whole list, because it might be
333                          * _really_ long and we probably don't want to clobber
334                          * the log with it.
335                          */
336                         nsd_logprintf(NSD_LOG_MIN, "    %s (...)\n", grp_name);
337             
338                         gr++;
339                     }
340                 }
341             
342                 entries = response.data.num_entries;
343                 free_response(&response);
344                 if (entries < MAX_GETPWENT_USERS)
345                     return(do_list(2,rq));
346                 else
347                     return(do_list(1,rq));
348             }
349
350             case WINBINDD_GETPWENT:
351             {
352                 int entries;
353
354                 nsd_logprintf(NSD_LOG_MIN,
355                         "callback (winbind) GETPWENT  %d responses\n",
356                         response.data.num_entries);
357
358                 if (response.data.num_entries) {
359                     struct winbindd_pw *pw = &response.data.pw;
360                     int i;
361
362                     pw = (struct winbindd_pw *)response.extra_data.data;
363                     if (! pw ) {
364                         nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
365                         free_response(&response);
366                         return NSD_ERROR;
367                     }
368                     for (i = 0; i < response.data.num_entries; i++) {
369                         result = NULL;
370                         rlen = asprintf(&result, "%s:%s:%d:%d:%s:%s:%s",
371                                         pw->pw_name,
372                                         pw->pw_passwd,
373                                         pw->pw_uid,
374                                         pw->pw_gid,
375                                         pw->pw_gecos,
376                                         pw->pw_dir,
377                                         pw->pw_shell);
378
379                         if (rlen != 0 && result != NULL) {
380                             nsd_logprintf(NSD_LOG_MIN, "    %s\n",result);
381                             nsd_append_element(rq, NS_SUCCESS, result, rlen);
382                             free(result);
383                         }
384
385                         pw++;
386                     }
387                 }
388
389                 entries = response.data.num_entries;
390                 free_response(&response);
391                 if (entries < MAX_GETPWENT_USERS)
392                     return(do_list(2,rq));
393                 else
394                     return(do_list(1,rq));
395             }
396
397             case WINBINDD_ENDGRENT:
398             case WINBINDD_ENDPWENT:
399                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) ENDGRENT | ENDPWENT\n");
400                 nsd_append_element(rq, NS_SUCCESS, "\n", 1);
401                 free_response(&response);
402                 return NSD_NEXT;
403
404             default:
405                 free_response(&response);
406                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) invalid command %d\n", (int)rq->f_cmd_data);
407                 return NSD_NEXT;
408         }
409 }
410
411 static int 
412 winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
413 {
414         nsd_file_t *rq;
415
416         dequeue_request();
417
418         nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
419
420         rq = to->t_file;
421         *rqp = rq;
422
423         /* Remove the callback and timeout */
424         nsd_callback_remove(winbindd_fd);
425         nsd_timeout_remove(rq);
426
427         rq->f_status = NS_NOTFOUND;
428         return NSD_NEXT;
429 }
430
431 static int
432 send_next_request(nsd_file_t *rq, struct winbindd_request *request)
433 {
434         NSS_STATUS status;
435         long timeout;
436
437         switch (rq->f_index) {
438                 case LOOKUP:
439                         timeout = nsd_attr_fetch_long(rq->f_attrs,
440                                         "lookup_timeout", 10, 10);
441                         break;
442                 case LIST:
443                         timeout = nsd_attr_fetch_long(rq->f_attrs,
444                                         "list_timeout", 10, 10);
445                         break;
446                 default:
447                         nsd_logprintf(NSD_LOG_OPER,
448                                 "send_next_request (winbind) "
449                                 "invalid request type %d\n", rq->f_index);
450                         rq->f_status = NS_BADREQ;
451                         return NSD_NEXT;
452         }
453
454         nsd_logprintf(NSD_LOG_MIN,
455                 "send_next_request (winbind) %d, timeout = %d sec\n",
456                         rq->f_cmd_data, timeout);
457         status = winbindd_send_request((int)rq->f_cmd_data,request);
458         SAFE_FREE(request);
459
460         if (status != NSS_STATUS_SUCCESS) {
461                 nsd_logprintf(NSD_LOG_MIN, 
462                         "send_next_request (winbind) error status = %d\n",
463                         status);
464                 rq->f_status = status;
465                 return NSD_NEXT;
466         }
467
468         current_rq = rq;
469
470         /*
471          * Set up callback and timeouts
472          */
473         nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",
474                 winbindd_fd);
475
476         nsd_callback_new(winbindd_fd, winbind_callback, NSD_READ);
477         nsd_timeout_new(rq, timeout * 1000, winbind_timeout, NULL);
478         return NSD_CONTINUE;
479 }
480
481 int init(void)
482 {
483         nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
484         return(NSD_OK);
485 }
486
487 int lookup(nsd_file_t *rq)
488 {
489         char *map;
490         char *key;
491         struct winbindd_request *request;
492
493         nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
494         if (! rq)
495                 return NSD_ERROR;
496
497         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
498         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
499         if (! map || ! key) {
500                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
501                 rq->f_status = NS_BADREQ;
502                 return NSD_ERROR;
503         }
504
505         nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
506
507         request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
508         if (! request) {
509                 nsd_logprintf(NSD_LOG_RESOURCE,
510                         "lookup (winbind): failed malloc\n");
511                 return NSD_ERROR;
512         }
513
514         if (strcasecmp(map,"passwd.byuid") == 0) {
515             request->data.uid = atoi(key);
516             rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
517         } else if (strcasecmp(map,"passwd.byname") == 0) {
518             strncpy(request->data.username, key, 
519                 sizeof(request->data.username) - 1);
520             request->data.username[sizeof(request->data.username) - 1] = '\0';
521             rq->f_cmd_data = (void *)WINBINDD_GETPWNAM; 
522         } else if (strcasecmp(map,"group.byname") == 0) {
523             strncpy(request->data.groupname, key, 
524                 sizeof(request->data.groupname) - 1);
525             request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
526             rq->f_cmd_data = (void *)WINBINDD_GETGRNAM; 
527         } else if (strcasecmp(map,"group.bygid") == 0) {
528             request->data.gid = atoi(key);
529             rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
530         } else if (strcasecmp(map,"hosts.byname") == 0) {
531             strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
532             request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
533             rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
534         } else if (strcasecmp(map,"hosts.byaddr") == 0) {
535             strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
536             request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
537             rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
538         } else {
539                 /*
540                  * Don't understand this map - just return not found
541                  */
542                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
543                 SAFE_FREE(request);
544                 rq->f_status = NS_NOTFOUND;
545                 return NSD_NEXT;
546         }
547
548         return(do_request(rq, request));
549 }
550
551 int list(nsd_file_t *rq)
552 {
553         char *map;
554
555         nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
556         if (! rq)
557                 return NSD_ERROR;
558
559         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
560         if (! map ) {
561                 nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
562                 rq->f_status = NS_BADREQ;
563                 return NSD_ERROR;
564         }
565
566         nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
567
568         return (do_list(0,rq));
569 }
570
571 static int
572 do_list(int state, nsd_file_t *rq)
573 {
574         char *map;
575         struct winbindd_request *request;
576
577         nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
578
579         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
580         request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
581         if (! request) {
582                 nsd_logprintf(NSD_LOG_RESOURCE,
583                         "do_list (winbind): failed malloc\n");
584                 return NSD_ERROR;
585         }
586
587         if (strcasecmp(map,"passwd.byname") == 0) {
588             switch (state) {
589                 case 0:
590                     rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
591                     break;
592                 case 1:
593                     request->data.num_entries = MAX_GETPWENT_USERS;
594                     rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
595                     break;
596                 case 2:
597                     rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
598                     break;
599                 default:
600                     nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
601                     SAFE_FREE(request);
602                     rq->f_status = NS_NOTFOUND;
603                     return NSD_NEXT;
604             }
605         } else if (strcasecmp(map,"group.byname") == 0) {
606             switch (state) {
607                 case 0:
608                     rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
609                     break;
610                 case 1:
611                     request->data.num_entries = MAX_GETGRENT_USERS;
612                     rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
613                     break;
614                 case 2:
615                     rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
616                     break;
617                 default:
618                     nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
619                     SAFE_FREE(request);
620                     rq->f_status = NS_NOTFOUND;
621                     return NSD_NEXT;
622             }
623         } else {
624                 /*
625                  * Don't understand this map - just return not found
626                  */
627                 nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
628                 SAFE_FREE(request);
629                 rq->f_status = NS_NOTFOUND;
630                 return NSD_NEXT;
631         }
632
633         return(do_request(rq, request));
634 }
635
636 #endif /* HAVE_NS_API_H */