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