r10656: BIG merge from trunk. Features not copied over
[Samba/nascimento.git] / source3 / nsswitch / winbindd_dual.c
blob60b7411417226b2b65fadc67f90da5e91b35deb9
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind background daemon
6 Copyright (C) Andrew Tridgell 2002
7 Copyright (C) Volker Lendecke 2004,2005
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.
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.
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.
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
33 #include "includes.h"
34 #include "winbindd.h"
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_WINBIND
39 /* Read some data from a client connection */
41 static void child_read_request(struct winbindd_cli_state *state)
43 ssize_t len;
45 /* Read data */
47 len = read_data(state->sock, (char *)&state->request,
48 sizeof(state->request));
50 if (len != sizeof(state->request)) {
51 DEBUG(0, ("Got invalid request length: %d\n", (int)len));
52 state->finished = True;
53 return;
56 if (state->request.extra_len == 0) {
57 state->request.extra_data = NULL;
58 return;
61 DEBUG(10, ("Need to read %d extra bytes\n", (int)state->request.extra_len));
63 state->request.extra_data =
64 SMB_MALLOC_ARRAY(char, state->request.extra_len + 1);
66 if (state->request.extra_data == NULL) {
67 DEBUG(0, ("malloc failed\n"));
68 state->finished = True;
69 return;
72 /* Ensure null termination */
73 state->request.extra_data[state->request.extra_len] = '\0';
75 len = read_data(state->sock, state->request.extra_data,
76 state->request.extra_len);
78 if (len != state->request.extra_len) {
79 DEBUG(0, ("Could not read extra data\n"));
80 state->finished = True;
81 return;
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.
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;
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);
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)
113 struct winbindd_async_request *state, *tmp;
115 SMB_ASSERT(continuation != NULL);
117 state = TALLOC_P(mem_ctx, struct winbindd_async_request);
119 if (state == NULL) {
120 DEBUG(0, ("talloc failed\n"));
121 continuation(private_data, False);
122 return;
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;
132 DLIST_ADD_END(child->requests, state, tmp);
134 schedule_async_request(child);
136 return;
139 static void async_main_request_sent(void *private_data, BOOL success)
141 struct winbindd_async_request *state =
142 talloc_get_type_abort(private_data, struct winbindd_async_request);
144 if (!success) {
145 DEBUG(5, ("Could not send async request\n"));
147 state->response->length = sizeof(struct winbindd_response);
148 state->response->result = WINBINDD_ERROR;
149 state->continuation(state->private_data, False);
150 return;
153 if (state->request->extra_len == 0) {
154 async_request_sent(private_data, True);
155 return;
158 setup_async_write(&state->child->event, state->request->extra_data,
159 state->request->extra_len,
160 async_request_sent, state);
163 static void async_request_sent(void *private_data_data, BOOL success)
165 struct winbindd_async_request *state =
166 talloc_get_type_abort(private_data_data, struct winbindd_async_request);
168 if (!success) {
169 DEBUG(5, ("Could not send async request\n"));
171 state->response->length = sizeof(struct winbindd_response);
172 state->response->result = WINBINDD_ERROR;
173 state->continuation(state->private_data, False);
174 return;
177 /* Request successfully sent to the child, setup the wait for reply */
179 setup_async_read(&state->child->event,
180 &state->response->result,
181 sizeof(state->response->result),
182 async_reply_recv, state);
185 static void async_reply_recv(void *private_data, BOOL success)
187 struct winbindd_async_request *state =
188 talloc_get_type_abort(private_data, struct winbindd_async_request);
189 struct winbindd_child *child = state->child;
191 state->response->length = sizeof(struct winbindd_response);
193 if (!success) {
194 DEBUG(5, ("Could not receive async reply\n"));
195 state->response->result = WINBINDD_ERROR;
196 return;
199 SMB_ASSERT(cache_retrieve_response(child->pid,
200 state->response));
202 DLIST_REMOVE(child->requests, state);
204 schedule_async_request(child);
206 state->continuation(state->private_data, True);
209 static BOOL fork_domain_child(struct winbindd_child *child);
211 static void schedule_async_request(struct winbindd_child *child)
213 struct winbindd_async_request *request = child->requests;
215 if (request == NULL) {
216 return;
219 if (child->event.flags != 0) {
220 return; /* Busy */
223 if ((child->pid == 0) && (!fork_domain_child(child))) {
224 /* Cancel all outstanding requests */
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;
232 return;
235 setup_async_write(&child->event, request->request,
236 sizeof(*request->request),
237 async_main_request_sent, request);
238 return;
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;
250 static void domain_init_recv(void *private_data_data, BOOL success);
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)
259 struct domain_request_state *state;
261 if (domain->initialized) {
262 async_request(mem_ctx, &domain->child, request, response,
263 continuation, private_data_data);
264 return;
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;
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;
281 init_child_connection(domain, domain_init_recv, state);
284 static void recvfrom_child(void *private_data_data, BOOL success)
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;
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. */
294 state->response.result = WINBINDD_PENDING;
296 if ((!success) || (result != WINBINDD_OK)) {
297 request_error(state);
298 return;
301 request_ok(state);
304 void sendto_child(struct winbindd_cli_state *state,
305 struct winbindd_child *child)
307 async_request(state->mem_ctx, child, &state->request,
308 &state->response, recvfrom_child, state);
311 void sendto_domain(struct winbindd_cli_state *state,
312 struct winbindd_domain *domain)
314 async_domain_request(state->mem_ctx, domain,
315 &state->request, &state->response,
316 recvfrom_child, state);
319 static void domain_init_recv(void *private_data_data, BOOL success)
321 struct domain_request_state *state =
322 talloc_get_type_abort(private_data_data, struct domain_request_state);
324 if (!success) {
325 DEBUG(5, ("Domain init returned an error\n"));
326 state->continuation(state->private_data_data, False);
327 return;
330 async_request(state->mem_ctx, &state->domain->child,
331 state->request, state->response,
332 state->continuation, state->private_data_data);
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;
342 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
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 */
367 { WINBINDD_NUM_CMDS, NULL, "NONE" }
370 static void child_process_request(struct winbindd_domain *domain,
371 struct winbindd_cli_state *state)
373 struct winbindd_child_dispatch_table *table;
375 /* Free response data - we may be interrupted and receive another
376 command before being able to send this data off. */
378 state->response.result = WINBINDD_ERROR;
379 state->response.length = sizeof(struct winbindd_response);
381 state->mem_ctx = talloc_init("winbind request");
382 if (state->mem_ctx == NULL)
383 return;
385 /* Process command */
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;
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;
402 talloc_destroy(state->mem_ctx);
405 void setup_domain_child(struct winbindd_domain *domain,
406 struct winbindd_child *child,
407 const char *explicit_logfile)
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");
420 child->domain = domain;
423 struct winbindd_child *children = NULL;
425 void winbind_child_died(pid_t pid)
427 struct winbindd_child *child;
429 for (child = children; child != NULL; child = child->next) {
430 if (child->pid == pid) {
431 break;
435 if (child == NULL) {
436 DEBUG(0, ("Unknown child %d died!\n", pid));
437 return;
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;
446 schedule_async_request(child);
449 static BOOL fork_domain_child(struct winbindd_child *child)
451 int fdpair[2];
452 struct winbindd_cli_state state;
453 extern BOOL override_logfile;
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;
461 ZERO_STRUCT(state);
462 state.pid = getpid();
464 child->pid = sys_fork();
466 if (child->pid == -1) {
467 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
468 return False;
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;
483 /* Child */
485 state.sock = fdpair[0];
486 close(fdpair[1]);
488 /* tdb needs special fork handling */
489 if (tdb_reopen_all() == -1) {
490 DEBUG(0,("tdb_reopen_all failed.\n"));
491 _exit(0);
494 close_conns_after_fork();
496 if (!override_logfile) {
497 lp_set_logfile(child->logfilename);
498 reopen_logs();
501 while (1) {
502 /* free up any talloc memory */
503 lp_talloc_free();
504 main_loop_talloc_free();
506 /* fetch a request from the main daemon */
507 child_read_request(&state);
509 if (state.finished) {
510 /* we lost contact with our parent */
511 exit(0);
514 DEBUG(4,("child daemon request %d\n", (int)state.request.cmd));
516 ZERO_STRUCT(state.response);
517 state.request.null_term = '\0';
518 child_process_request(child->domain, &state);
520 SAFE_FREE(state.request.extra_data);
522 cache_store_response(sys_getpid(), &state.response);
524 SAFE_FREE(state.response.extra_data);
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... */
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);