r10656: BIG merge from trunk. Features not copied over
[nivanova/samba-autobuild/.git] / source3 / 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 /* Read some data from a client connection */
40
41 static void child_read_request(struct winbindd_cli_state *state)
42 {
43         ssize_t len;
44
45         /* Read data */
46
47         len = read_data(state->sock, (char *)&state->request,
48                         sizeof(state->request));
49
50         if (len != sizeof(state->request)) {
51                 DEBUG(0, ("Got invalid request length: %d\n", (int)len));
52                 state->finished = True;
53                 return;
54         }
55
56         if (state->request.extra_len == 0) {
57                 state->request.extra_data = NULL;
58                 return;
59         }
60
61         DEBUG(10, ("Need to read %d extra bytes\n", (int)state->request.extra_len));
62
63         state->request.extra_data =
64                 SMB_MALLOC_ARRAY(char, state->request.extra_len + 1);
65
66         if (state->request.extra_data == NULL) {
67                 DEBUG(0, ("malloc failed\n"));
68                 state->finished = True;
69                 return;
70         }
71
72         /* Ensure null termination */
73         state->request.extra_data[state->request.extra_len] = '\0';
74
75         len = read_data(state->sock, state->request.extra_data,
76                         state->request.extra_len);
77
78         if (len != state->request.extra_len) {
79                 DEBUG(0, ("Could not read extra data\n"));
80                 state->finished = True;
81                 return;
82         }
83 }
84
85 /*
86  * Machinery for async requests sent to children. You set up a
87  * winbindd_request, select a child to query, and issue a async_request
88  * call. When the request is completed, the callback function you specified is
89  * called back with the private pointer you gave to async_request.
90  */
91
92 struct winbindd_async_request {
93         struct winbindd_async_request *next, *prev;
94         TALLOC_CTX *mem_ctx;
95         struct winbindd_child *child;
96         struct winbindd_request *request;
97         struct winbindd_response *response;
98         void (*continuation)(void *private_data, BOOL success);
99         void *private_data;
100 };
101
102 static void async_main_request_sent(void *private_data, BOOL success);
103 static void async_request_sent(void *private_data, BOOL success);
104 static void async_reply_recv(void *private_data, BOOL success);
105 static void schedule_async_request(struct winbindd_child *child);
106
107 void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
108                    struct winbindd_request *request,
109                    struct winbindd_response *response,
110                    void (*continuation)(void *private_data, BOOL success),
111                    void *private_data)
112 {
113         struct winbindd_async_request *state, *tmp;
114
115         SMB_ASSERT(continuation != NULL);
116
117         state = TALLOC_P(mem_ctx, struct winbindd_async_request);
118
119         if (state == NULL) {
120                 DEBUG(0, ("talloc failed\n"));
121                 continuation(private_data, False);
122                 return;
123         }
124
125         state->mem_ctx = mem_ctx;
126         state->child = child;
127         state->request = request;
128         state->response = response;
129         state->continuation = continuation;
130         state->private_data = private_data;
131
132         DLIST_ADD_END(child->requests, state, tmp);
133
134         schedule_async_request(child);
135
136         return;
137 }
138
139 static void async_main_request_sent(void *private_data, BOOL success)
140 {
141         struct winbindd_async_request *state =
142                 talloc_get_type_abort(private_data, struct winbindd_async_request);
143
144         if (!success) {
145                 DEBUG(5, ("Could not send async request\n"));
146
147                 state->response->length = sizeof(struct winbindd_response);
148                 state->response->result = WINBINDD_ERROR;
149                 state->continuation(state->private_data, False);
150                 return;
151         }
152
153         if (state->request->extra_len == 0) {
154                 async_request_sent(private_data, True);
155                 return;
156         }
157
158         setup_async_write(&state->child->event, state->request->extra_data,
159                           state->request->extra_len,
160                           async_request_sent, state);
161 }
162
163 static void async_request_sent(void *private_data_data, BOOL success)
164 {
165         struct winbindd_async_request *state =
166                 talloc_get_type_abort(private_data_data, struct winbindd_async_request);
167
168         if (!success) {
169                 DEBUG(5, ("Could not send async request\n"));
170
171                 state->response->length = sizeof(struct winbindd_response);
172                 state->response->result = WINBINDD_ERROR;
173                 state->continuation(state->private_data, False);
174                 return;
175         }
176
177         /* Request successfully sent to the child, setup the wait for reply */
178
179         setup_async_read(&state->child->event,
180                          &state->response->result,
181                          sizeof(state->response->result),
182                          async_reply_recv, state);
183 }
184
185 static void async_reply_recv(void *private_data, BOOL success)
186 {
187         struct winbindd_async_request *state =
188                 talloc_get_type_abort(private_data, struct winbindd_async_request);
189         struct winbindd_child *child = state->child;
190
191         state->response->length = sizeof(struct winbindd_response);
192
193         if (!success) {
194                 DEBUG(5, ("Could not receive async reply\n"));
195                 state->response->result = WINBINDD_ERROR;
196                 return;
197         }
198
199         SMB_ASSERT(cache_retrieve_response(child->pid,
200                                            state->response));
201
202         DLIST_REMOVE(child->requests, state);
203
204         schedule_async_request(child);
205
206         state->continuation(state->private_data, True);
207 }
208
209 static BOOL fork_domain_child(struct winbindd_child *child);
210
211 static void schedule_async_request(struct winbindd_child *child)
212 {
213         struct winbindd_async_request *request = child->requests;
214
215         if (request == NULL) {
216                 return;
217         }
218
219         if (child->event.flags != 0) {
220                 return;         /* Busy */
221         }
222
223         if ((child->pid == 0) && (!fork_domain_child(child))) {
224                 /* Cancel all outstanding requests */
225
226                 while (request != NULL) {
227                         /* request might be free'd in the continuation */
228                         struct winbindd_async_request *next = request->next;
229                         request->continuation(request->private_data, False);
230                         request = next;
231                 }
232                 return;
233         }
234
235         setup_async_write(&child->event, request->request,
236                           sizeof(*request->request),
237                           async_main_request_sent, request);
238         return;
239 }
240
241 struct domain_request_state {
242         TALLOC_CTX *mem_ctx;
243         struct winbindd_domain *domain;
244         struct winbindd_request *request;
245         struct winbindd_response *response;
246         void (*continuation)(void *private_data_data, BOOL success);
247         void *private_data_data;
248 };
249
250 static void domain_init_recv(void *private_data_data, BOOL success);
251
252 void async_domain_request(TALLOC_CTX *mem_ctx,
253                           struct winbindd_domain *domain,
254                           struct winbindd_request *request,
255                           struct winbindd_response *response,
256                           void (*continuation)(void *private_data_data, BOOL success),
257                           void *private_data_data)
258 {
259         struct domain_request_state *state;
260
261         if (domain->initialized) {
262                 async_request(mem_ctx, &domain->child, request, response,
263                               continuation, private_data_data);
264                 return;
265         }
266
267         state = TALLOC_P(mem_ctx, struct domain_request_state);
268         if (state == NULL) {
269                 DEBUG(0, ("talloc failed\n"));
270                 continuation(private_data_data, False);
271                 return;
272         }
273
274         state->mem_ctx = mem_ctx;
275         state->domain = domain;
276         state->request = request;
277         state->response = response;
278         state->continuation = continuation;
279         state->private_data_data = private_data_data;
280
281         init_child_connection(domain, domain_init_recv, state);
282 }
283
284 static void recvfrom_child(void *private_data_data, BOOL success)
285 {
286         struct winbindd_cli_state *state =
287                 talloc_get_type_abort(private_data_data, struct winbindd_cli_state);
288         enum winbindd_result result = state->response.result;
289
290         /* This is an optimization: The child has written directly to the
291          * response buffer. The request itself is still in pending state,
292          * state that in the result code. */
293
294         state->response.result = WINBINDD_PENDING;
295
296         if ((!success) || (result != WINBINDD_OK)) {
297                 request_error(state);
298                 return;
299         }
300
301         request_ok(state);
302 }
303
304 void sendto_child(struct winbindd_cli_state *state,
305                   struct winbindd_child *child)
306 {
307         async_request(state->mem_ctx, child, &state->request,
308                       &state->response, recvfrom_child, state);
309 }
310
311 void sendto_domain(struct winbindd_cli_state *state,
312                    struct winbindd_domain *domain)
313 {
314         async_domain_request(state->mem_ctx, domain,
315                              &state->request, &state->response,
316                              recvfrom_child, state);
317 }
318
319 static void domain_init_recv(void *private_data_data, BOOL success)
320 {
321         struct domain_request_state *state =
322                 talloc_get_type_abort(private_data_data, struct domain_request_state);
323
324         if (!success) {
325                 DEBUG(5, ("Domain init returned an error\n"));
326                 state->continuation(state->private_data_data, False);
327                 return;
328         }
329
330         async_request(state->mem_ctx, &state->domain->child,
331                       state->request, state->response,
332                       state->continuation, state->private_data_data);
333 }
334
335 struct winbindd_child_dispatch_table {
336         enum winbindd_cmd cmd;
337         enum winbindd_result (*fn)(struct winbindd_domain *domain,
338                                    struct winbindd_cli_state *state);
339         const char *winbindd_cmd_name;
340 };
341
342 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
343         
344         { WINBINDD_LOOKUPSID,            winbindd_dual_lookupsid,             "LOOKUPSID" },
345         { WINBINDD_LOOKUPNAME,           winbindd_dual_lookupname,            "LOOKUPNAME" },
346         { WINBINDD_LIST_TRUSTDOM,        winbindd_dual_list_trusted_domains,  "LIST_TRUSTDOM" },
347         { WINBINDD_INIT_CONNECTION,      winbindd_dual_init_connection,       "INIT_CONNECTION" },
348         { WINBINDD_GETDCNAME,            winbindd_dual_getdcname,             "GETDCNAME" },
349         { WINBINDD_SHOW_SEQUENCE,        winbindd_dual_show_sequence,         "SHOW_SEQUENCE" },
350         { WINBINDD_PAM_AUTH,             winbindd_dual_pam_auth,              "PAM_AUTH" },
351         { WINBINDD_PAM_AUTH_CRAP,        winbindd_dual_pam_auth_crap,         "AUTH_CRAP" },
352         { WINBINDD_CHECK_MACHACC,        winbindd_dual_check_machine_acct,    "CHECK_MACHACC" },
353         { WINBINDD_DUAL_SID2UID,         winbindd_dual_sid2uid,               "DUAL_SID2UID" },
354         { WINBINDD_DUAL_SID2GID,         winbindd_dual_sid2gid,               "DUAL_SID2GID" },
355         { WINBINDD_DUAL_UID2NAME,        winbindd_dual_uid2name,              "DUAL_UID2NAME" },
356         { WINBINDD_DUAL_NAME2UID,        winbindd_dual_name2uid,              "DUAL_NAME2UID" },
357         { WINBINDD_DUAL_GID2NAME,        winbindd_dual_gid2name,              "DUAL_GID2NAME" },
358         { WINBINDD_DUAL_NAME2GID,        winbindd_dual_name2gid,              "DUAL_NAME2GID" },
359         { WINBINDD_DUAL_IDMAPSET,        winbindd_dual_idmapset,              "DUAL_IDMAPSET" },
360         { WINBINDD_DUAL_USERINFO,        winbindd_dual_userinfo,              "DUAL_USERINFO" },
361         { WINBINDD_ALLOCATE_RID,         winbindd_dual_allocate_rid,          "ALLOCATE_RID" },
362         { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid,  "ALLOCATE_RID_AND_GID" },
363         { WINBINDD_GETUSERDOMGROUPS,     winbindd_dual_getuserdomgroups,      "GETUSERDOMGROUPS" },
364         { WINBINDD_DUAL_GETSIDALIASES,   winbindd_dual_getsidaliases,         "GETSIDALIASES" },
365         /* End of list */
366
367         { WINBINDD_NUM_CMDS, NULL, "NONE" }
368 };
369
370 static void child_process_request(struct winbindd_domain *domain,
371                                   struct winbindd_cli_state *state)
372 {
373         struct winbindd_child_dispatch_table *table;
374
375         /* Free response data - we may be interrupted and receive another
376            command before being able to send this data off. */
377
378         state->response.result = WINBINDD_ERROR;
379         state->response.length = sizeof(struct winbindd_response);
380
381         state->mem_ctx = talloc_init("winbind request");
382         if (state->mem_ctx == NULL)
383                 return;
384
385         /* Process command */
386
387         for (table = child_dispatch_table; table->fn; table++) {
388                 if (state->request.cmd == table->cmd) {
389                         DEBUG(10,("process_request: request fn %s\n",
390                                   table->winbindd_cmd_name ));
391                         state->response.result = table->fn(domain, state);
392                         break;
393                 }
394         }
395
396         if (!table->fn) {
397                 DEBUG(10,("process_request: unknown request fn number %d\n",
398                           (int)state->request.cmd ));
399                 state->response.result = WINBINDD_ERROR;
400         }
401
402         talloc_destroy(state->mem_ctx);
403 }
404
405 void setup_domain_child(struct winbindd_domain *domain,
406                         struct winbindd_child *child,
407                         const char *explicit_logfile)
408 {
409         if (explicit_logfile != NULL) {
410                 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
411                              dyn_LOGFILEBASE, explicit_logfile);
412         } else if (domain != NULL) {
413                 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
414                              dyn_LOGFILEBASE, domain->name);
415         } else {
416                 smb_panic("Internal error: domain == NULL && "
417                           "explicit_logfile == NULL");
418         }
419
420         child->domain = domain;
421 }
422
423 struct winbindd_child *children = NULL;
424
425 void winbind_child_died(pid_t pid)
426 {
427         struct winbindd_child *child;
428
429         for (child = children; child != NULL; child = child->next) {
430                 if (child->pid == pid) {
431                         break;
432                 }
433         }
434
435         if (child == NULL) {
436                 DEBUG(0, ("Unknown child %d died!\n", pid));
437                 return;
438         }
439
440         remove_fd_event(&child->event);
441         close(child->event.fd);
442         child->event.fd = 0;
443         child->event.flags = 0;
444         child->pid = 0;
445
446         schedule_async_request(child);
447 }
448
449 static BOOL fork_domain_child(struct winbindd_child *child)
450 {
451         int fdpair[2];
452         struct winbindd_cli_state state;
453         extern BOOL override_logfile;
454
455         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
456                 DEBUG(0, ("Could not open child pipe: %s\n",
457                           strerror(errno)));
458                 return False;
459         }
460
461         ZERO_STRUCT(state);
462         state.pid = getpid();
463
464         child->pid = sys_fork();
465
466         if (child->pid == -1) {
467                 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
468                 return False;
469         }
470
471         if (child->pid != 0) {
472                 /* Parent */
473                 close(fdpair[0]);
474                 child->next = child->prev = NULL;
475                 DLIST_ADD(children, child);
476                 child->event.fd = fdpair[1];
477                 child->event.flags = 0;
478                 child->requests = NULL;
479                 add_fd_event(&child->event);
480                 return True;
481         }
482
483         /* Child */
484
485         state.sock = fdpair[0];
486         close(fdpair[1]);
487
488         /* tdb needs special fork handling */
489         if (tdb_reopen_all() == -1) {
490                 DEBUG(0,("tdb_reopen_all failed.\n"));
491                 _exit(0);
492         }
493
494         close_conns_after_fork();
495
496         if (!override_logfile) {
497                 lp_set_logfile(child->logfilename);
498                 reopen_logs();
499         }
500         
501         while (1) {
502                 /* free up any talloc memory */
503                 lp_talloc_free();
504                 main_loop_talloc_free();
505
506                 /* fetch a request from the main daemon */
507                 child_read_request(&state);
508
509                 if (state.finished) {
510                         /* we lost contact with our parent */
511                         exit(0);
512                 }
513
514                 DEBUG(4,("child daemon request %d\n", (int)state.request.cmd));
515
516                 ZERO_STRUCT(state.response);
517                 state.request.null_term = '\0';
518                 child_process_request(child->domain, &state);
519
520                 SAFE_FREE(state.request.extra_data);
521
522                 cache_store_response(sys_getpid(), &state.response);
523
524                 SAFE_FREE(state.response.extra_data);
525
526                 /* We just send the result code back, the result
527                  * structure needs to be fetched via the
528                  * winbindd_cache. Hmm. That needs fixing... */
529
530                 if (write_data(state.sock, (void *)&state.response.result,
531                                sizeof(state.response.result)) !=
532                     sizeof(state.response.result)) {
533                         DEBUG(0, ("Could not write result\n"));
534                         exit(1);
535                 }
536         }
537 }