r7423: Fix the build on sol10
[kai/samba-autobuild/.git] / source / nsswitch / winbindd_dual.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind background daemon
5
6    Copyright (C) Andrew Tridgell 2002
7    Copyright (C) Volker Lendecke 2004,2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program 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
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 /*
25   the idea of the optional dual daemon mode is ot prevent slow domain
26   responses from clagging up the rest of the system. When in dual
27   daemon mode winbindd always responds to requests from cache if the
28   request is in cache, and if the cached answer is stale then it asks
29   the "dual daemon" to update the cache for that request
30
31  */
32
33 #include "includes.h"
34 #include "winbindd.h"
35
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_WINBIND
38
39 extern BOOL opt_dual_daemon;
40 BOOL background_process = False;
41 int dual_daemon_pipe = -1;
42
43
44 /* a list of requests ready to be sent to the dual daemon */
45 struct dual_list {
46         struct dual_list *next;
47         char *data;
48         int length;
49         int offset;
50 };
51
52 static struct dual_list *dual_list;
53 static struct dual_list *dual_list_end;
54
55 /* Read some data from a client connection */
56
57 static void dual_client_read(struct winbindd_cli_state *state)
58 {
59         int n;
60     
61         /* Read data */
62
63         n = sys_read(state->sock, state->read_buf_len + 
64                  (char *)&state->request, 
65                  sizeof(state->request) - state->read_buf_len);
66         
67         DEBUG(10,("client_read: read %d bytes. Need %ld more for a full "
68                   "request.\n", n, (unsigned long)(sizeof(state->request) - n -
69                                                    state->read_buf_len) ));
70
71         /* Read failed, kill client */
72         
73         if (n == -1 || n == 0) {
74                 DEBUG(5,("read failed on sock %d, pid %lu: %s\n",
75                          state->sock, (unsigned long)state->pid, 
76                          (n == -1) ? strerror(errno) : "EOF"));
77                 
78                 state->finished = True;
79                 return;
80         }
81         
82         /* Update client state */
83         
84         state->read_buf_len += n;
85         state->last_access = time(NULL);
86 }
87
88 /*
89   setup a select() including the dual daemon pipe
90  */
91 int dual_select_setup(fd_set *fds, int maxfd)
92 {
93         if (dual_daemon_pipe == -1 ||
94             !dual_list) {
95                 return maxfd;
96         }
97
98         FD_SET(dual_daemon_pipe, fds);
99         if (dual_daemon_pipe > maxfd) {
100                 maxfd = dual_daemon_pipe;
101         }
102         return maxfd;
103 }
104
105
106 /*
107   a hook called from the main winbindd select() loop to handle writes
108   to the dual daemon pipe 
109 */
110 void dual_select(fd_set *fds)
111 {
112         int n;
113
114         if (dual_daemon_pipe == -1 ||
115             !dual_list ||
116             !FD_ISSET(dual_daemon_pipe, fds)) {
117                 return;
118         }
119
120         n = sys_write(dual_daemon_pipe, 
121                   &dual_list->data[dual_list->offset],
122                   dual_list->length - dual_list->offset);
123
124         if (n <= 0) {
125                 /* the pipe is dead! fall back to normal operation */
126                 dual_daemon_pipe = -1;
127                 return;
128         }
129
130         dual_list->offset += n;
131
132         if (dual_list->offset == dual_list->length) {
133                 struct dual_list *next;
134                 next = dual_list->next;
135                 free(dual_list->data);
136                 free(dual_list);
137                 dual_list = next;
138                 if (!dual_list) {
139                         dual_list_end = NULL;
140                 }
141         }
142 }
143
144 /* 
145    send a request to the background daemon 
146    this is called for stale cached entries
147 */
148 void dual_send_request(struct winbindd_cli_state *state)
149 {
150         struct dual_list *list;
151
152         if (!background_process) return;
153
154         list = SMB_MALLOC_P(struct dual_list);
155         if (!list) return;
156
157         list->next = NULL;
158         list->data = memdup(&state->request, sizeof(state->request));
159         list->length = sizeof(state->request);
160         list->offset = 0;
161         
162         if (!dual_list_end) {
163                 dual_list = list;
164                 dual_list_end = list;
165         } else {
166                 dual_list_end->next = list;
167                 dual_list_end = list;
168         }
169
170         background_process = False;
171 }
172
173
174 /* 
175 the main dual daemon 
176 */
177 void do_dual_daemon(void)
178 {
179         int fdpair[2];
180         struct winbindd_cli_state state;
181         
182         if (pipe(fdpair) != 0) {
183                 return;
184         }
185
186         ZERO_STRUCT(state);
187         state.pid = getpid();
188
189         dual_daemon_pipe = fdpair[1];
190         state.sock = fdpair[0];
191
192         if (sys_fork() != 0) {
193                 close(fdpair[0]);
194                 return;
195         }
196         close(fdpair[1]);
197
198         /* tdb needs special fork handling */
199         if (tdb_reopen_all() == -1) {
200                 DEBUG(0,("tdb_reopen_all failed.\n"));
201                 _exit(0);
202         }
203         
204         dual_daemon_pipe = -1;
205         opt_dual_daemon = False;
206
207         while (1) {
208                 /* free up any talloc memory */
209                 lp_talloc_free();
210                 main_loop_talloc_free();
211
212                 /* fetch a request from the main daemon */
213                 dual_client_read(&state);
214
215                 if (state.finished) {
216                         /* we lost contact with our parent */
217                         exit(0);
218                 }
219
220                 /* process full rquests */
221                 if (state.read_buf_len == sizeof(state.request)) {
222                         DEBUG(4,("dual daemon request %d\n", (int)state.request.cmd));
223
224                         /* special handling for the stateful requests */
225                         switch (state.request.cmd) {
226                         case WINBINDD_GETPWENT:
227                                 winbindd_setpwent(&state);
228                                 break;
229                                 
230                         case WINBINDD_GETGRENT:
231                         case WINBINDD_GETGRLST:
232                                 winbindd_setgrent(&state);
233                                 break;
234                         default:
235                                 break;
236                         }
237
238                         winbind_process_packet(&state);
239                         SAFE_FREE(state.response.extra_data);
240
241                         free_getent_state(state.getpwent_state);
242                         free_getent_state(state.getgrent_state);
243                         state.getpwent_state = NULL;
244                         state.getgrent_state = NULL;
245                 }
246         }
247 }
248
249 /*
250  * Machinery for async requests sent to children. You set up a
251  * winbindd_request, select a child to query, and issue a async_request
252  * call. When the request is completed, the callback function you specified is
253  * called back with the private pointer you gave to async_request.
254  */
255
256 struct winbindd_async_request {
257         struct winbindd_async_request *next, *prev;
258         TALLOC_CTX *mem_ctx;
259         struct winbindd_child *child;
260         struct winbindd_request *request;
261         struct winbindd_response *response;
262         void (*continuation)(void *private, BOOL success);
263         void *private;
264 };
265
266 static void async_request_sent(void *private, BOOL success);
267 static void async_reply_recv(void *private, BOOL success);
268 static void schedule_async_request(struct winbindd_child *child);
269
270 void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
271                    struct winbindd_request *request,
272                    struct winbindd_response *response,
273                    void (*continuation)(void *private, BOOL success),
274                    void *private)
275 {
276         struct winbindd_async_request *state, *tmp;
277
278         SMB_ASSERT(continuation != NULL);
279
280         state = TALLOC_P(mem_ctx, struct winbindd_async_request);
281
282         if (state == NULL) {
283                 DEBUG(0, ("talloc failed\n"));
284                 continuation(private, False);
285                 return;
286         }
287
288         state->mem_ctx = mem_ctx;
289         state->child = child;
290         state->request = request;
291         state->response = response;
292         state->continuation = continuation;
293         state->private = private;
294
295         DLIST_ADD_END(child->requests, state, tmp);
296
297         schedule_async_request(child);
298
299         return;
300 }
301
302 static void async_request_sent(void *private, BOOL success)
303 {
304         struct winbindd_async_request *state =
305                 talloc_get_type_abort(private, struct winbindd_async_request);
306
307         if (!success) {
308                 DEBUG(5, ("Could not send async request\n"));
309
310                 state->response->length = sizeof(struct winbindd_response);
311                 state->response->result = WINBINDD_ERROR;
312                 state->continuation(state->private, False);
313                 return;
314         }
315
316         /* Request successfully sent to the child, setup the wait for reply */
317
318         setup_async_read(&state->child->event,
319                          &state->response->result,
320                          sizeof(state->response->result),
321                          async_reply_recv, state);
322 }
323
324 static void async_reply_recv(void *private, BOOL success)
325 {
326         struct winbindd_async_request *state =
327                 talloc_get_type_abort(private, struct winbindd_async_request);
328         struct winbindd_child *child = state->child;
329
330         state->response->length = sizeof(struct winbindd_response);
331
332         if (!success) {
333                 DEBUG(5, ("Could not receive async reply\n"));
334                 state->response->result = WINBINDD_ERROR;
335         }
336
337         if (state->response->result == WINBINDD_OK)
338                 SMB_ASSERT(cache_retrieve_response(child->pid,
339                                                    state->response));
340
341         DLIST_REMOVE(child->requests, state);
342
343         schedule_async_request(child);
344
345         state->continuation(state->private, True);
346 }
347
348 static BOOL fork_domain_child(struct winbindd_child *child);
349
350 static void schedule_async_request(struct winbindd_child *child)
351 {
352         struct winbindd_async_request *request = child->requests;
353
354         if (request == NULL) {
355                 return;
356         }
357
358         if (child->event.flags != 0) {
359                 return;         /* Busy */
360         }
361
362         if ((child->pid == 0) && (!fork_domain_child(child))) {
363                 /* Cancel all outstanding requests */
364
365                 while (request != NULL) {
366                         /* request might be free'd in the continuation */
367                         struct winbindd_async_request *next = request->next;
368                         request->continuation(request->private, False);
369                         request = next;
370                 }
371                 return;
372         }
373
374         setup_async_write(&child->event, request->request,
375                           sizeof(*request->request),
376                           async_request_sent, request);
377         return;
378 }
379
380 struct domain_request_state {
381         TALLOC_CTX *mem_ctx;
382         struct winbindd_domain *domain;
383         struct winbindd_request *request;
384         struct winbindd_response *response;
385         void (*continuation)(void *private, BOOL success);
386         void *private;
387 };
388
389 static void domain_init_recv(void *private, BOOL success);
390
391 void async_domain_request(TALLOC_CTX *mem_ctx,
392                           struct winbindd_domain *domain,
393                           struct winbindd_request *request,
394                           struct winbindd_response *response,
395                           void (*continuation)(void *private, BOOL success),
396                           void *private)
397 {
398         struct domain_request_state *state;
399
400         if (domain->initialized) {
401                 async_request(mem_ctx, &domain->child, request, response,
402                               continuation, private);
403                 return;
404         }
405
406         state = TALLOC_P(mem_ctx, struct domain_request_state);
407         if (state == NULL) {
408                 DEBUG(0, ("talloc failed\n"));
409                 continuation(private, False);
410                 return;
411         }
412
413         state->mem_ctx = mem_ctx;
414         state->domain = domain;
415         state->request = request;
416         state->response = response;
417         state->continuation = continuation;
418         state->private = private;
419
420         init_child_connection(domain, domain_init_recv, state);
421 }
422
423 static void domain_init_recv(void *private, BOOL success)
424 {
425         struct domain_request_state *state =
426                 talloc_get_type_abort(private, struct domain_request_state);
427
428         if (!success) {
429                 DEBUG(5, ("Domain init returned an error\n"));
430                 state->continuation(state->private, False);
431                 return;
432         }
433
434         async_request(state->mem_ctx, &state->domain->child,
435                       state->request, state->response,
436                       state->continuation, state->private);
437 }
438
439 struct winbindd_child_dispatch_table {
440         enum winbindd_cmd cmd;
441         enum winbindd_result (*fn)(struct winbindd_domain *domain,
442                                    struct winbindd_cli_state *state);
443         const char *winbindd_cmd_name;
444 };
445
446 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
447         
448         { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" },
449         { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" },
450         { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains,
451           "LIST_TRUSTDOM" },
452         { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection,
453           "INIT_CONNECTION" },
454         { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
455         { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence,
456           "SHOW_SEQUENCE" },
457         { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" },
458         { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" },
459         { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct,
460           "CHECK_MACHACC" },
461         { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" },
462         { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" },
463         { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" },
464         { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" },
465         { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" },
466         { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" },
467         { WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" },
468         { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" },
469         { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" },
470         { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid,
471           "ALLOCATE_RID_AND_GID" },
472         { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups,
473           "GETUSERDOMGROUPS" },
474         { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases,
475           "GETSIDALIASES" },
476         /* End of list */
477
478         { WINBINDD_NUM_CMDS, NULL, "NONE" }
479 };
480
481 static void child_process_request(struct winbindd_domain *domain,
482                                   struct winbindd_cli_state *state)
483 {
484         struct winbindd_child_dispatch_table *table;
485
486         /* Free response data - we may be interrupted and receive another
487            command before being able to send this data off. */
488
489         state->response.result = WINBINDD_ERROR;
490         state->response.length = sizeof(struct winbindd_response);
491
492         state->mem_ctx = talloc_init("winbind request");
493         if (state->mem_ctx == NULL)
494                 return;
495
496         /* Process command */
497
498         for (table = child_dispatch_table; table->fn; table++) {
499                 if (state->request.cmd == table->cmd) {
500                         DEBUG(10,("process_request: request fn %s\n",
501                                   table->winbindd_cmd_name ));
502                         state->response.result = table->fn(domain, state);
503                         break;
504                 }
505         }
506
507         if (!table->fn) {
508                 DEBUG(10,("process_request: unknown request fn number %d\n",
509                           (int)state->request.cmd ));
510                 state->response.result = WINBINDD_ERROR;
511         }
512
513         talloc_destroy(state->mem_ctx);
514 }
515
516 void setup_domain_child(struct winbindd_domain *domain,
517                         struct winbindd_child *child,
518                         const char *explicit_logfile)
519 {
520         if (explicit_logfile != NULL) {
521                 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
522                              dyn_LOGFILEBASE, explicit_logfile);
523         } else if (domain != NULL) {
524                 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
525                              dyn_LOGFILEBASE, domain->name);
526         } else {
527                 smb_panic("Internal error: domain == NULL && "
528                           "explicit_logfile == NULL");
529         }
530
531         child->domain = domain;
532 }
533
534 struct winbindd_child *children = NULL;
535
536 void winbind_child_died(pid_t pid)
537 {
538         struct winbindd_child *child;
539
540         for (child = children; child != NULL; child = child->next) {
541                 if (child->pid == pid) {
542                         break;
543                 }
544         }
545
546         if (child == NULL) {
547                 DEBUG(0, ("Unknown child %d died!\n", pid));
548                 return;
549         }
550
551         remove_fd_event(&child->event);
552         close(child->event.fd);
553         child->event.fd = 0;
554         child->event.flags = 0;
555         child->pid = 0;
556
557         schedule_async_request(child);
558 }
559
560 static BOOL fork_domain_child(struct winbindd_child *child)
561 {
562         int fdpair[2];
563         struct winbindd_cli_state state;
564         extern BOOL override_logfile;
565
566         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
567                 DEBUG(0, ("Could not open child pipe: %s\n",
568                           strerror(errno)));
569                 return False;
570         }
571
572         ZERO_STRUCT(state);
573         state.pid = getpid();
574
575         child->pid = sys_fork();
576
577         if (child->pid == -1) {
578                 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
579                 return False;
580         }
581
582         if (child->pid != 0) {
583                 /* Parent */
584                 close(fdpair[0]);
585                 child->next = child->prev = NULL;
586                 DLIST_ADD(children, child);
587                 child->event.fd = fdpair[1];
588                 child->event.flags = 0;
589                 child->requests = NULL;
590                 add_fd_event(&child->event);
591                 return True;
592         }
593
594         /* Child */
595
596         state.sock = fdpair[0];
597         close(fdpair[1]);
598
599         /* tdb needs special fork handling */
600         if (tdb_reopen_all() == -1) {
601                 DEBUG(0,("tdb_reopen_all failed.\n"));
602                 _exit(0);
603         }
604
605         close_conns_after_fork();
606
607         if (!override_logfile) {
608                 lp_set_logfile(child->logfilename);
609                 reopen_logs();
610         }
611         
612         dual_daemon_pipe = -1;
613         opt_dual_daemon = False;
614
615         while (1) {
616                 /* free up any talloc memory */
617                 lp_talloc_free();
618                 main_loop_talloc_free();
619
620                 /* fetch a request from the main daemon */
621                 dual_client_read(&state);
622
623                 if (state.finished) {
624                         /* we lost contact with our parent */
625                         exit(0);
626                 }
627
628                 /* process full rquests */
629                 if (state.read_buf_len == sizeof(state.request)) {
630                         DEBUG(4,("child daemon request %d\n",
631                                  (int)state.request.cmd));
632
633                         state.request.null_term = '\0';
634                         child_process_request(child->domain, &state);
635
636                         if (state.response.result == WINBINDD_OK)
637                                 cache_store_response(sys_getpid(),
638                                                      &state.response);
639
640                         SAFE_FREE(state.response.extra_data);
641
642                         /* We just send the result code back, the result
643                          * structure needs to be fetched via the
644                          * winbindd_cache. Hmm. That needs fixing... */
645
646                         if (write_data(state.sock,
647                                        (void *)&state.response.result,
648                                        sizeof(state.response.result)) !=
649                             sizeof(state.response.result)) {
650                                 DEBUG(0, ("Could not write result\n"));
651                                 exit(1);
652                         }
653
654                         state.read_buf_len = 0;
655                 }
656         }
657 }