r7785: This looks much larger than it is. It changes the top-level functions of the
[tprouty/samba.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                 return;
336         }
337
338         if (state->response->result == WINBINDD_OK)
339                 SMB_ASSERT(cache_retrieve_response(child->pid,
340                                                    state->response));
341
342         DLIST_REMOVE(child->requests, state);
343
344         schedule_async_request(child);
345
346         state->continuation(state->private, True);
347 }
348
349 static BOOL fork_domain_child(struct winbindd_child *child);
350
351 static void schedule_async_request(struct winbindd_child *child)
352 {
353         struct winbindd_async_request *request = child->requests;
354
355         if (request == NULL) {
356                 return;
357         }
358
359         if (child->event.flags != 0) {
360                 return;         /* Busy */
361         }
362
363         if ((child->pid == 0) && (!fork_domain_child(child))) {
364                 /* Cancel all outstanding requests */
365
366                 while (request != NULL) {
367                         /* request might be free'd in the continuation */
368                         struct winbindd_async_request *next = request->next;
369                         request->continuation(request->private, False);
370                         request = next;
371                 }
372                 return;
373         }
374
375         setup_async_write(&child->event, request->request,
376                           sizeof(*request->request),
377                           async_request_sent, request);
378         return;
379 }
380
381 struct domain_request_state {
382         TALLOC_CTX *mem_ctx;
383         struct winbindd_domain *domain;
384         struct winbindd_request *request;
385         struct winbindd_response *response;
386         void (*continuation)(void *private, BOOL success);
387         void *private;
388 };
389
390 static void domain_init_recv(void *private, BOOL success);
391
392 void async_domain_request(TALLOC_CTX *mem_ctx,
393                           struct winbindd_domain *domain,
394                           struct winbindd_request *request,
395                           struct winbindd_response *response,
396                           void (*continuation)(void *private, BOOL success),
397                           void *private)
398 {
399         struct domain_request_state *state;
400
401         if (domain->initialized) {
402                 async_request(mem_ctx, &domain->child, request, response,
403                               continuation, private);
404                 return;
405         }
406
407         state = TALLOC_P(mem_ctx, struct domain_request_state);
408         if (state == NULL) {
409                 DEBUG(0, ("talloc failed\n"));
410                 continuation(private, False);
411                 return;
412         }
413
414         state->mem_ctx = mem_ctx;
415         state->domain = domain;
416         state->request = request;
417         state->response = response;
418         state->continuation = continuation;
419         state->private = private;
420
421         init_child_connection(domain, domain_init_recv, state);
422 }
423
424 static void recvfrom_child(void *private, BOOL success)
425 {
426         struct winbindd_cli_state *state =
427                 talloc_get_type_abort(private, struct winbindd_cli_state);
428         enum winbindd_result result = state->response.result;
429
430         /* This is an optimization: The child has written directly to the
431          * response buffer. The request itself is still in pending state,
432          * state that in the result code. */
433
434         state->response.result = WINBINDD_PENDING;
435
436         if ((!success) || (result != WINBINDD_OK)) {
437                 request_error(state);
438                 return;
439         }
440
441         request_ok(state);
442 }
443
444 void sendto_child(struct winbindd_cli_state *state,
445                   struct winbindd_child *child)
446 {
447         async_request(state->mem_ctx, child, &state->request,
448                       &state->response, recvfrom_child, state);
449 }
450
451 void sendto_domain(struct winbindd_cli_state *state,
452                    struct winbindd_domain *domain)
453 {
454         async_domain_request(state->mem_ctx, domain,
455                              &state->request, &state->response,
456                              recvfrom_child, state);
457 }
458
459 static void domain_init_recv(void *private, BOOL success)
460 {
461         struct domain_request_state *state =
462                 talloc_get_type_abort(private, struct domain_request_state);
463
464         if (!success) {
465                 DEBUG(5, ("Domain init returned an error\n"));
466                 state->continuation(state->private, False);
467                 return;
468         }
469
470         async_request(state->mem_ctx, &state->domain->child,
471                       state->request, state->response,
472                       state->continuation, state->private);
473 }
474
475 struct winbindd_child_dispatch_table {
476         enum winbindd_cmd cmd;
477         enum winbindd_result (*fn)(struct winbindd_domain *domain,
478                                    struct winbindd_cli_state *state);
479         const char *winbindd_cmd_name;
480 };
481
482 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
483         
484         { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" },
485         { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" },
486         { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains,
487           "LIST_TRUSTDOM" },
488         { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection,
489           "INIT_CONNECTION" },
490         { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
491         { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence,
492           "SHOW_SEQUENCE" },
493         { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" },
494         { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" },
495         { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct,
496           "CHECK_MACHACC" },
497         { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" },
498         { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" },
499         { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" },
500         { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" },
501         { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" },
502         { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" },
503         { WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" },
504         { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" },
505         { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" },
506         { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid,
507           "ALLOCATE_RID_AND_GID" },
508         { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups,
509           "GETUSERDOMGROUPS" },
510         { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases,
511           "GETSIDALIASES" },
512         /* End of list */
513
514         { WINBINDD_NUM_CMDS, NULL, "NONE" }
515 };
516
517 static void child_process_request(struct winbindd_domain *domain,
518                                   struct winbindd_cli_state *state)
519 {
520         struct winbindd_child_dispatch_table *table;
521
522         /* Free response data - we may be interrupted and receive another
523            command before being able to send this data off. */
524
525         state->response.result = WINBINDD_ERROR;
526         state->response.length = sizeof(struct winbindd_response);
527
528         state->mem_ctx = talloc_init("winbind request");
529         if (state->mem_ctx == NULL)
530                 return;
531
532         /* Process command */
533
534         for (table = child_dispatch_table; table->fn; table++) {
535                 if (state->request.cmd == table->cmd) {
536                         DEBUG(10,("process_request: request fn %s\n",
537                                   table->winbindd_cmd_name ));
538                         state->response.result = table->fn(domain, state);
539                         break;
540                 }
541         }
542
543         if (!table->fn) {
544                 DEBUG(10,("process_request: unknown request fn number %d\n",
545                           (int)state->request.cmd ));
546                 state->response.result = WINBINDD_ERROR;
547         }
548
549         talloc_destroy(state->mem_ctx);
550 }
551
552 void setup_domain_child(struct winbindd_domain *domain,
553                         struct winbindd_child *child,
554                         const char *explicit_logfile)
555 {
556         if (explicit_logfile != NULL) {
557                 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
558                              dyn_LOGFILEBASE, explicit_logfile);
559         } else if (domain != NULL) {
560                 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
561                              dyn_LOGFILEBASE, domain->name);
562         } else {
563                 smb_panic("Internal error: domain == NULL && "
564                           "explicit_logfile == NULL");
565         }
566
567         child->domain = domain;
568 }
569
570 struct winbindd_child *children = NULL;
571
572 void winbind_child_died(pid_t pid)
573 {
574         struct winbindd_child *child;
575
576         for (child = children; child != NULL; child = child->next) {
577                 if (child->pid == pid) {
578                         break;
579                 }
580         }
581
582         if (child == NULL) {
583                 DEBUG(0, ("Unknown child %d died!\n", pid));
584                 return;
585         }
586
587         remove_fd_event(&child->event);
588         close(child->event.fd);
589         child->event.fd = 0;
590         child->event.flags = 0;
591         child->pid = 0;
592
593         schedule_async_request(child);
594 }
595
596 static BOOL fork_domain_child(struct winbindd_child *child)
597 {
598         int fdpair[2];
599         struct winbindd_cli_state state;
600         extern BOOL override_logfile;
601
602         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
603                 DEBUG(0, ("Could not open child pipe: %s\n",
604                           strerror(errno)));
605                 return False;
606         }
607
608         ZERO_STRUCT(state);
609         state.pid = getpid();
610
611         child->pid = sys_fork();
612
613         if (child->pid == -1) {
614                 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
615                 return False;
616         }
617
618         if (child->pid != 0) {
619                 /* Parent */
620                 close(fdpair[0]);
621                 child->next = child->prev = NULL;
622                 DLIST_ADD(children, child);
623                 child->event.fd = fdpair[1];
624                 child->event.flags = 0;
625                 child->requests = NULL;
626                 add_fd_event(&child->event);
627                 return True;
628         }
629
630         /* Child */
631
632         state.sock = fdpair[0];
633         close(fdpair[1]);
634
635         /* tdb needs special fork handling */
636         if (tdb_reopen_all() == -1) {
637                 DEBUG(0,("tdb_reopen_all failed.\n"));
638                 _exit(0);
639         }
640
641         close_conns_after_fork();
642
643         if (!override_logfile) {
644                 lp_set_logfile(child->logfilename);
645                 reopen_logs();
646         }
647         
648         dual_daemon_pipe = -1;
649         opt_dual_daemon = False;
650
651         while (1) {
652                 /* free up any talloc memory */
653                 lp_talloc_free();
654                 main_loop_talloc_free();
655
656                 /* fetch a request from the main daemon */
657                 dual_client_read(&state);
658
659                 if (state.finished) {
660                         /* we lost contact with our parent */
661                         exit(0);
662                 }
663
664                 /* process full rquests */
665                 if (state.read_buf_len == sizeof(state.request)) {
666                         DEBUG(4,("child daemon request %d\n",
667                                  (int)state.request.cmd));
668
669                         state.request.null_term = '\0';
670                         child_process_request(child->domain, &state);
671
672                         if (state.response.result == WINBINDD_OK)
673                                 cache_store_response(sys_getpid(),
674                                                      &state.response);
675
676                         SAFE_FREE(state.response.extra_data);
677
678                         /* We just send the result code back, the result
679                          * structure needs to be fetched via the
680                          * winbindd_cache. Hmm. That needs fixing... */
681
682                         if (write_data(state.sock,
683                                        (void *)&state.response.result,
684                                        sizeof(state.response.result)) !=
685                             sizeof(state.response.result)) {
686                                 DEBUG(0, ("Could not write result\n"));
687                                 exit(1);
688                         }
689
690                         state.read_buf_len = 0;
691                 }
692         }
693 }