r9362: formatting cleanup
[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 /* 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         if (state->response->result == WINBINDD_OK)
162                 SMB_ASSERT(cache_retrieve_response(child->pid,
163                                                    state->response));
164
165         DLIST_REMOVE(child->requests, state);
166
167         schedule_async_request(child);
168
169         state->continuation(state->private_data, True);
170 }
171
172 static BOOL fork_domain_child(struct winbindd_child *child);
173
174 static void schedule_async_request(struct winbindd_child *child)
175 {
176         struct winbindd_async_request *request = child->requests;
177
178         if (request == NULL) {
179                 return;
180         }
181
182         if (child->event.flags != 0) {
183                 return;         /* Busy */
184         }
185
186         if ((child->pid == 0) && (!fork_domain_child(child))) {
187                 /* Cancel all outstanding requests */
188
189                 while (request != NULL) {
190                         /* request might be free'd in the continuation */
191                         struct winbindd_async_request *next = request->next;
192                         request->continuation(request->private_data, False);
193                         request = next;
194                 }
195                 return;
196         }
197
198         setup_async_write(&child->event, request->request,
199                           sizeof(*request->request),
200                           async_request_sent, request);
201         return;
202 }
203
204 struct domain_request_state {
205         TALLOC_CTX *mem_ctx;
206         struct winbindd_domain *domain;
207         struct winbindd_request *request;
208         struct winbindd_response *response;
209         void (*continuation)(void *private_data, BOOL success);
210         void *private_data;
211 };
212
213 static void domain_init_recv(void *private_data, BOOL success);
214
215 void async_domain_request(TALLOC_CTX *mem_ctx,
216                           struct winbindd_domain *domain,
217                           struct winbindd_request *request,
218                           struct winbindd_response *response,
219                           void (*continuation)(void *private_data, BOOL success),
220                           void *private_data)
221 {
222         struct domain_request_state *state;
223
224         if (domain->initialized) {
225                 async_request(mem_ctx, &domain->child, request, response,
226                               continuation, private_data);
227                 return;
228         }
229
230         state = TALLOC_P(mem_ctx, struct domain_request_state);
231         if (state == NULL) {
232                 DEBUG(0, ("talloc failed\n"));
233                 continuation(private_data, False);
234                 return;
235         }
236
237         state->mem_ctx = mem_ctx;
238         state->domain = domain;
239         state->request = request;
240         state->response = response;
241         state->continuation = continuation;
242         state->private_data = private_data;
243
244         init_child_connection(domain, domain_init_recv, state);
245 }
246
247 static void recvfrom_child(void *private_data, BOOL success)
248 {
249         struct winbindd_cli_state *state =
250                 talloc_get_type_abort(private_data, struct winbindd_cli_state);
251         enum winbindd_result result = state->response.result;
252
253         /* This is an optimization: The child has written directly to the
254          * response buffer. The request itself is still in pending state,
255          * state that in the result code. */
256
257         state->response.result = WINBINDD_PENDING;
258
259         if ((!success) || (result != WINBINDD_OK)) {
260                 request_error(state);
261                 return;
262         }
263
264         request_ok(state);
265 }
266
267 void sendto_child(struct winbindd_cli_state *state,
268                   struct winbindd_child *child)
269 {
270         async_request(state->mem_ctx, child, &state->request,
271                       &state->response, recvfrom_child, state);
272 }
273
274 void sendto_domain(struct winbindd_cli_state *state,
275                    struct winbindd_domain *domain)
276 {
277         async_domain_request(state->mem_ctx, domain,
278                              &state->request, &state->response,
279                              recvfrom_child, state);
280 }
281
282 static void domain_init_recv(void *private_data, BOOL success)
283 {
284         struct domain_request_state *state =
285                 talloc_get_type_abort(private_data, struct domain_request_state);
286
287         if (!success) {
288                 DEBUG(5, ("Domain init returned an error\n"));
289                 state->continuation(state->private_data, False);
290                 return;
291         }
292
293         async_request(state->mem_ctx, &state->domain->child,
294                       state->request, state->response,
295                       state->continuation, state->private_data);
296 }
297
298 struct winbindd_child_dispatch_table {
299         enum winbindd_cmd cmd;
300         enum winbindd_result (*fn)(struct winbindd_domain *domain,
301                                    struct winbindd_cli_state *state);
302         const char *winbindd_cmd_name;
303 };
304
305 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
306         
307         { WINBINDD_LOOKUPSID,            winbindd_dual_lookupsid,             "LOOKUPSID" },
308         { WINBINDD_LOOKUPNAME,           winbindd_dual_lookupname,            "LOOKUPNAME" },
309         { WINBINDD_LIST_TRUSTDOM,        winbindd_dual_list_trusted_domains,  "LIST_TRUSTDOM" },
310         { WINBINDD_INIT_CONNECTION,      winbindd_dual_init_connection,       "INIT_CONNECTION" },
311         { WINBINDD_GETDCNAME,            winbindd_dual_getdcname,             "GETDCNAME" },
312         { WINBINDD_SHOW_SEQUENCE,        winbindd_dual_show_sequence,         "SHOW_SEQUENCE" },
313         { WINBINDD_PAM_AUTH,             winbindd_dual_pam_auth,              "PAM_AUTH" },
314         { WINBINDD_PAM_AUTH_CRAP,        winbindd_dual_pam_auth_crap,         "AUTH_CRAP" },
315         { WINBINDD_CHECK_MACHACC,        winbindd_dual_check_machine_acct,    "CHECK_MACHACC" },
316         { WINBINDD_DUAL_SID2UID,         winbindd_dual_sid2uid,               "DUAL_SID2UID" },
317         { WINBINDD_DUAL_SID2GID,         winbindd_dual_sid2gid,               "DUAL_SID2GID" },
318         { WINBINDD_DUAL_UID2NAME,        winbindd_dual_uid2name,              "DUAL_UID2NAME" },
319         { WINBINDD_DUAL_NAME2UID,        winbindd_dual_name2uid,              "DUAL_NAME2UID" },
320         { WINBINDD_DUAL_GID2NAME,        winbindd_dual_gid2name,              "DUAL_GID2NAME" },
321         { WINBINDD_DUAL_NAME2GID,        winbindd_dual_name2gid,              "DUAL_NAME2GID" },
322         { WINBINDD_DUAL_IDMAPSET,        winbindd_dual_idmapset,              "DUAL_IDMAPSET" },
323         { WINBINDD_DUAL_USERINFO,        winbindd_dual_userinfo,              "DUAL_USERINFO" },
324         { WINBINDD_ALLOCATE_RID,         winbindd_dual_allocate_rid,          "ALLOCATE_RID" },
325         { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid,  "ALLOCATE_RID_AND_GID" },
326         { WINBINDD_GETUSERDOMGROUPS,     winbindd_dual_getuserdomgroups,      "GETUSERDOMGROUPS" },
327         { WINBINDD_DUAL_GETSIDALIASES,   winbindd_dual_getsidaliases,         "GETSIDALIASES" },
328         /* End of list */
329
330         { WINBINDD_NUM_CMDS, NULL, "NONE" }
331 };
332
333 static void child_process_request(struct winbindd_domain *domain,
334                                   struct winbindd_cli_state *state)
335 {
336         struct winbindd_child_dispatch_table *table;
337
338         /* Free response data - we may be interrupted and receive another
339            command before being able to send this data off. */
340
341         state->response.result = WINBINDD_ERROR;
342         state->response.length = sizeof(struct winbindd_response);
343
344         state->mem_ctx = talloc_init("winbind request");
345         if (state->mem_ctx == NULL)
346                 return;
347
348         /* Process command */
349
350         for (table = child_dispatch_table; table->fn; table++) {
351                 if (state->request.cmd == table->cmd) {
352                         DEBUG(10,("process_request: request fn %s\n",
353                                   table->winbindd_cmd_name ));
354                         state->response.result = table->fn(domain, state);
355                         break;
356                 }
357         }
358
359         if (!table->fn) {
360                 DEBUG(10,("process_request: unknown request fn number %d\n",
361                           (int)state->request.cmd ));
362                 state->response.result = WINBINDD_ERROR;
363         }
364
365         talloc_destroy(state->mem_ctx);
366 }
367
368 void setup_domain_child(struct winbindd_domain *domain,
369                         struct winbindd_child *child,
370                         const char *explicit_logfile)
371 {
372         if (explicit_logfile != NULL) {
373                 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
374                              dyn_LOGFILEBASE, explicit_logfile);
375         } else if (domain != NULL) {
376                 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
377                              dyn_LOGFILEBASE, domain->name);
378         } else {
379                 smb_panic("Internal error: domain == NULL && "
380                           "explicit_logfile == NULL");
381         }
382
383         child->domain = domain;
384 }
385
386 struct winbindd_child *children = NULL;
387
388 void winbind_child_died(pid_t pid)
389 {
390         struct winbindd_child *child;
391
392         for (child = children; child != NULL; child = child->next) {
393                 if (child->pid == pid) {
394                         break;
395                 }
396         }
397
398         if (child == NULL) {
399                 DEBUG(0, ("Unknown child %d died!\n", pid));
400                 return;
401         }
402
403         remove_fd_event(&child->event);
404         close(child->event.fd);
405         child->event.fd = 0;
406         child->event.flags = 0;
407         child->pid = 0;
408
409         schedule_async_request(child);
410 }
411
412 static BOOL fork_domain_child(struct winbindd_child *child)
413 {
414         int fdpair[2];
415         struct winbindd_cli_state state;
416         extern BOOL override_logfile;
417
418         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
419                 DEBUG(0, ("Could not open child pipe: %s\n",
420                           strerror(errno)));
421                 return False;
422         }
423
424         ZERO_STRUCT(state);
425         state.pid = getpid();
426
427         child->pid = sys_fork();
428
429         if (child->pid == -1) {
430                 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
431                 return False;
432         }
433
434         if (child->pid != 0) {
435                 /* Parent */
436                 close(fdpair[0]);
437                 child->next = child->prev = NULL;
438                 DLIST_ADD(children, child);
439                 child->event.fd = fdpair[1];
440                 child->event.flags = 0;
441                 child->requests = NULL;
442                 add_fd_event(&child->event);
443                 return True;
444         }
445
446         /* Child */
447
448         state.sock = fdpair[0];
449         close(fdpair[1]);
450
451         /* tdb needs special fork handling */
452         if (tdb_reopen_all() == -1) {
453                 DEBUG(0,("tdb_reopen_all failed.\n"));
454                 _exit(0);
455         }
456
457         close_conns_after_fork();
458
459         if (!override_logfile) {
460                 lp_set_logfile(child->logfilename);
461                 reopen_logs();
462         }
463         
464         while (1) {
465                 /* free up any talloc memory */
466                 lp_talloc_free();
467                 main_loop_talloc_free();
468
469                 /* fetch a request from the main daemon */
470                 dual_client_read(&state);
471
472                 if (state.finished) {
473                         /* we lost contact with our parent */
474                         exit(0);
475                 }
476
477                 /* process full rquests */
478                 if (state.read_buf_len == sizeof(state.request)) {
479                         DEBUG(4,("child daemon request %d\n",
480                                  (int)state.request.cmd));
481
482                         state.request.null_term = '\0';
483                         child_process_request(child->domain, &state);
484
485                         if (state.response.result == WINBINDD_OK)
486                                 cache_store_response(sys_getpid(),
487                                                      &state.response);
488
489                         SAFE_FREE(state.response.extra_data);
490
491                         /* We just send the result code back, the result
492                          * structure needs to be fetched via the
493                          * winbindd_cache. Hmm. That needs fixing... */
494
495                         if (write_data(state.sock,
496                                        (void *)&state.response.result,
497                                        sizeof(state.response.result)) !=
498                             sizeof(state.response.result)) {
499                                 DEBUG(0, ("Could not write result\n"));
500                                 exit(1);
501                         }
502
503                         state.read_buf_len = 0;
504                 }
505         }
506 }