s3-waf: make most of the nsswitch common.
[samba.git] / 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 Lesser General Public
11    License as published by the Free Software Foundation; either
12    version 3 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 Lesser General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "winbind_client.h"
24
25 #ifndef PRINTF_ATTRIBUTE
26 #define PRINTF_ATTRIBUTE(m, n)
27 #endif
28
29 #ifndef HAVE_ASPRINTF_DECL
30 /*PRINTFLIKE2 */
31 int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3);
32 #endif
33
34 #ifdef HAVE_NS_API_H
35 #undef STATIC
36 #undef DYNAMIC
37 #include <ns_daemon.h>
38 #endif
39
40 /* Maximum number of users to pass back over the unix domain socket
41    per call. This is not a static limit on the total number of users
42    or groups returned in total. */
43
44 #define MAX_GETPWENT_USERS 250
45 #define MAX_GETGRENT_USERS 250
46
47 /* Prototypes from wb_common.c */
48
49 extern int winbindd_fd;
50
51 #ifdef HAVE_NS_API_H
52
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(void)
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         nsd_file_t *rq;
178         NSS_STATUS status;
179         char * result = NULL;
180         size_t rlen;
181
182         dequeue_request();
183
184         nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
185
186         rq = current_rq;
187         *rqp = rq;
188
189         nsd_timeout_remove(rq);
190         nsd_callback_remove(fd);
191
192         ZERO_STRUCT(response);
193         status = winbindd_get_response(&response);
194
195         if (status != NSS_STATUS_SUCCESS) {
196                 /* free any extra data area in response structure */
197                 winbindd_free_response(&response);
198                 nsd_logprintf(NSD_LOG_MIN,
199                         "callback (winbind) returning not found, status = %d\n",
200                         status);
201
202                 switch (status) {
203                     case NSS_STATUS_UNAVAIL:
204                         rq->f_status = NS_UNAVAIL;
205                         break;
206                     case NSS_STATUS_TRYAGAIN:
207                         rq->f_status = NS_TRYAGAIN;
208                         break;
209                     case NSS_STATUS_NOTFOUND:
210                         /* FALLTHRU */
211                     default:
212                         rq->f_status = NS_NOTFOUND;
213                 }
214
215                 return NSD_NEXT;
216         }
217
218         switch ((int)rq->f_cmd_data) {
219             case WINBINDD_WINS_BYNAME:
220             case WINBINDD_WINS_BYIP:
221                 nsd_logprintf(NSD_LOG_MIN,
222                         "callback (winbind) WINS_BYNAME | WINS_BYIP\n");
223
224                 rlen = asprintf(&result, "%s\n", response.data.winsresp);
225                 if (rlen == 0 || result == NULL) {
226                         return NSD_ERROR;
227                 }
228
229                 winbindd_free_response(&response);
230
231                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
232                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
233                 return NSD_OK;
234
235             case WINBINDD_GETPWUID:
236             case WINBINDD_GETPWNAM:
237             {
238                 struct winbindd_pw *pw = &response.data.pw;
239
240                 nsd_logprintf(NSD_LOG_MIN,
241                         "callback (winbind) GETPWUID | GETPWUID\n");
242
243                 rlen = asprintf(&result,"%s:%s:%d:%d:%s:%s:%s\n",
244                                 pw->pw_name,
245                                 pw->pw_passwd,
246                                 pw->pw_uid,
247                                 pw->pw_gid,
248                                 pw->pw_gecos,
249                                 pw->pw_dir,
250                                 pw->pw_shell);
251                 if (rlen == 0 || result == NULL)
252                     return NSD_ERROR;
253
254                 winbindd_free_response(&response);
255
256                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
257                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
258                 return NSD_OK;
259             }
260
261             case WINBINDD_GETGRNAM:
262             case WINBINDD_GETGRGID:
263             {
264                 const struct winbindd_gr *gr = &response.data.gr;
265                 const char * members;
266
267                 nsd_logprintf(NSD_LOG_MIN,
268                         "callback (winbind) GETGRNAM | GETGRGID\n");
269
270                 if (gr->num_gr_mem && response.extra_data.data) {
271                         members = response.extra_data.data;
272                 } else {
273                         members = "";
274                 }
275
276                 rlen = asprintf(&result, "%s:%s:%d:%s\n",
277                             gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
278                 if (rlen == 0 || result == NULL)
279                     return NSD_ERROR;
280
281                 winbindd_free_response(&response);
282
283                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
284                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
285                 return NSD_OK;
286             }
287
288             case WINBINDD_SETGRENT:
289             case WINBINDD_SETPWENT:
290                 nsd_logprintf(NSD_LOG_MIN,
291                         "callback (winbind) SETGRENT | SETPWENT\n");
292                 winbindd_free_response(&response);
293                 return(do_list(1,rq));
294
295             case WINBINDD_GETGRENT:
296             case WINBINDD_GETGRLST:
297             {
298                 int entries;
299
300                 nsd_logprintf(NSD_LOG_MIN,
301                     "callback (winbind) GETGRENT | GETGRLIST %d responses\n",
302                     response.data.num_entries);
303
304                 if (response.data.num_entries) {
305                     const struct winbindd_gr *gr = &response.data.gr;
306                     const char * members;
307                     fstring grp_name;
308                     int     i;
309
310                     gr = (struct winbindd_gr *)response.extra_data.data;
311                     if (! gr ) {
312                         nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
313                         winbindd_free_response(&response);
314                         return NSD_ERROR;
315                     }
316
317                     members = (char *)response.extra_data.data +
318                         (response.data.num_entries * sizeof(struct winbindd_gr));
319
320                     for (i = 0; i < response.data.num_entries; i++) {
321                         snprintf(grp_name, sizeof(grp_name) - 1, "%s:%s:%d:",
322                                     gr->gr_name, gr->gr_passwd, gr->gr_gid);
323
324                         nsd_append_element(rq, NS_SUCCESS, result, rlen);
325                         nsd_append_result(rq, NS_SUCCESS,
326                                 &members[gr->gr_mem_ofs],
327                                 strlen(&members[gr->gr_mem_ofs]));
328
329                         /* Don't log the whole list, because it might be
330                          * _really_ long and we probably don't want to clobber
331                          * the log with it.
332                          */
333                         nsd_logprintf(NSD_LOG_MIN, "    %s (...)\n", grp_name);
334
335                         gr++;
336                     }
337                 }
338
339                 entries = response.data.num_entries;
340                 winbindd_free_response(&response);
341                 if (entries < MAX_GETPWENT_USERS)
342                     return(do_list(2,rq));
343                 else
344                     return(do_list(1,rq));
345             }
346
347             case WINBINDD_GETPWENT:
348             {
349                 int entries;
350
351                 nsd_logprintf(NSD_LOG_MIN,
352                         "callback (winbind) GETPWENT  %d responses\n",
353                         response.data.num_entries);
354
355                 if (response.data.num_entries) {
356                     struct winbindd_pw *pw = &response.data.pw;
357                     int i;
358
359                     pw = (struct winbindd_pw *)response.extra_data.data;
360                     if (! pw ) {
361                         nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
362                         winbindd_free_response(&response);
363                         return NSD_ERROR;
364                     }
365                     for (i = 0; i < response.data.num_entries; i++) {
366                         result = NULL;
367                         rlen = asprintf(&result, "%s:%s:%d:%d:%s:%s:%s",
368                                         pw->pw_name,
369                                         pw->pw_passwd,
370                                         pw->pw_uid,
371                                         pw->pw_gid,
372                                         pw->pw_gecos,
373                                         pw->pw_dir,
374                                         pw->pw_shell);
375
376                         if (rlen != 0 && result != NULL) {
377                             nsd_logprintf(NSD_LOG_MIN, "    %s\n",result);
378                             nsd_append_element(rq, NS_SUCCESS, result, rlen);
379                             free(result);
380                         }
381
382                         pw++;
383                     }
384                 }
385
386                 entries = response.data.num_entries;
387                 winbindd_free_response(&response);
388                 if (entries < MAX_GETPWENT_USERS)
389                     return(do_list(2,rq));
390                 else
391                     return(do_list(1,rq));
392             }
393
394             case WINBINDD_ENDGRENT:
395             case WINBINDD_ENDPWENT:
396                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) ENDGRENT | ENDPWENT\n");
397                 nsd_append_element(rq, NS_SUCCESS, "\n", 1);
398                 winbindd_free_response(&response);
399                 return NSD_NEXT;
400
401             default:
402                 winbindd_free_response(&response);
403                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) invalid command %d\n", (int)rq->f_cmd_data);
404                 return NSD_NEXT;
405         }
406 }
407
408 static int
409 winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
410 {
411         nsd_file_t *rq;
412
413         dequeue_request();
414
415         nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
416
417         rq = to->t_file;
418         *rqp = rq;
419
420         /* Remove the callback and timeout */
421         nsd_callback_remove(winbindd_fd);
422         nsd_timeout_remove(rq);
423
424         rq->f_status = NS_NOTFOUND;
425         return NSD_NEXT;
426 }
427
428 static int
429 send_next_request(nsd_file_t *rq, struct winbindd_request *request)
430 {
431         NSS_STATUS status;
432         long timeout;
433
434         switch (rq->f_index) {
435                 case LOOKUP:
436                         timeout = nsd_attr_fetch_long(rq->f_attrs,
437                                         "lookup_timeout", 10, 10);
438                         break;
439                 case LIST:
440                         timeout = nsd_attr_fetch_long(rq->f_attrs,
441                                         "list_timeout", 10, 10);
442                         break;
443                 default:
444                         nsd_logprintf(NSD_LOG_OPER,
445                                 "send_next_request (winbind) "
446                                 "invalid request type %d\n", rq->f_index);
447                         rq->f_status = NS_BADREQ;
448                         return NSD_NEXT;
449         }
450
451         nsd_logprintf(NSD_LOG_MIN,
452                 "send_next_request (winbind) %d, timeout = %d sec\n",
453                         rq->f_cmd_data, timeout);
454         status = winbindd_send_request((int)rq->f_cmd_data,0,request);
455         SAFE_FREE(request);
456
457         if (status != NSS_STATUS_SUCCESS) {
458                 nsd_logprintf(NSD_LOG_MIN,
459                         "send_next_request (winbind) error status = %d\n",
460                         status);
461                 rq->f_status = status;
462                 return NSD_NEXT;
463         }
464
465         current_rq = rq;
466
467         /*
468          * Set up callback and timeouts
469          */
470         nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",
471                 winbindd_fd);
472
473         nsd_callback_new(winbindd_fd, winbind_callback, NSD_READ);
474         nsd_timeout_new(rq, timeout * 1000, winbind_timeout, NULL);
475         return NSD_CONTINUE;
476 }
477
478 int init(void)
479 {
480         nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
481         return(NSD_OK);
482 }
483
484 int lookup(nsd_file_t *rq)
485 {
486         char *map;
487         char *key;
488         struct winbindd_request *request;
489
490         nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
491         if (! rq)
492                 return NSD_ERROR;
493
494         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
495         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
496         if (! map || ! key) {
497                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
498                 rq->f_status = NS_BADREQ;
499                 return NSD_ERROR;
500         }
501
502         nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
503
504         request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
505         if (! request) {
506                 nsd_logprintf(NSD_LOG_RESOURCE,
507                         "lookup (winbind): failed malloc\n");
508                 return NSD_ERROR;
509         }
510
511         if (strcasecmp(map,"passwd.byuid") == 0) {
512             request->data.uid = atoi(key);
513             rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
514         } else if (strcasecmp(map,"passwd.byname") == 0) {
515             strncpy(request->data.username, key,
516                 sizeof(request->data.username) - 1);
517             request->data.username[sizeof(request->data.username) - 1] = '\0';
518             rq->f_cmd_data = (void *)WINBINDD_GETPWNAM;
519         } else if (strcasecmp(map,"group.byname") == 0) {
520             strncpy(request->data.groupname, key,
521                 sizeof(request->data.groupname) - 1);
522             request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
523             rq->f_cmd_data = (void *)WINBINDD_GETGRNAM;
524         } else if (strcasecmp(map,"group.bygid") == 0) {
525             request->data.gid = atoi(key);
526             rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
527         } else if (strcasecmp(map,"hosts.byname") == 0) {
528             strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
529             request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
530             rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
531         } else if (strcasecmp(map,"hosts.byaddr") == 0) {
532             strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
533             request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
534             rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
535         } else {
536                 /*
537                  * Don't understand this map - just return not found
538                  */
539                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
540                 SAFE_FREE(request);
541                 rq->f_status = NS_NOTFOUND;
542                 return NSD_NEXT;
543         }
544
545         return(do_request(rq, request));
546 }
547
548 int list(nsd_file_t *rq)
549 {
550         char *map;
551
552         nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
553         if (! rq)
554                 return NSD_ERROR;
555
556         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
557         if (! map ) {
558                 nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
559                 rq->f_status = NS_BADREQ;
560                 return NSD_ERROR;
561         }
562
563         nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
564
565         return (do_list(0,rq));
566 }
567
568 static int
569 do_list(int state, nsd_file_t *rq)
570 {
571         char *map;
572         struct winbindd_request *request;
573
574         nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
575
576         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
577         request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
578         if (! request) {
579                 nsd_logprintf(NSD_LOG_RESOURCE,
580                         "do_list (winbind): failed malloc\n");
581                 return NSD_ERROR;
582         }
583
584         if (strcasecmp(map,"passwd.byname") == 0) {
585             switch (state) {
586                 case 0:
587                     rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
588                     break;
589                 case 1:
590                     request->data.num_entries = MAX_GETPWENT_USERS;
591                     rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
592                     break;
593                 case 2:
594                     rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
595                     break;
596                 default:
597                     nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
598                     SAFE_FREE(request);
599                     rq->f_status = NS_NOTFOUND;
600                     return NSD_NEXT;
601             }
602         } else if (strcasecmp(map,"group.byname") == 0) {
603             switch (state) {
604                 case 0:
605                     rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
606                     break;
607                 case 1:
608                     request->data.num_entries = MAX_GETGRENT_USERS;
609                     rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
610                     break;
611                 case 2:
612                     rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
613                     break;
614                 default:
615                     nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
616                     SAFE_FREE(request);
617                     rq->f_status = NS_NOTFOUND;
618                     return NSD_NEXT;
619             }
620         } else {
621                 /*
622                  * Don't understand this map - just return not found
623                  */
624                 nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
625                 SAFE_FREE(request);
626                 rq->f_status = NS_NOTFOUND;
627                 return NSD_NEXT;
628         }
629
630         return(do_request(rq, request));
631 }
632
633 #endif /* HAVE_NS_API_H */