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
37 #define DBGC_CLASS DBGC_WINBIND
39 /* Read some data from a client connection */
41 static void dual_client_read(struct winbindd_cli_state *state)
47 n = sys_read(state->sock, state->read_buf_len +
48 (char *)&state->request,
49 sizeof(state->request) - state->read_buf_len);
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) ));
55 /* Read failed, kill client */
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"));
62 state->finished = True;
66 /* Update client state */
68 state->read_buf_len += n;
69 state->last_access = time(NULL);
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.
79 struct winbindd_async_request {
80 struct winbindd_async_request *next, *prev;
82 struct winbindd_child *child;
83 struct winbindd_request *request;
84 struct winbindd_response *response;
85 void (*continuation)(void *private_data, BOOL success);
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);
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),
99 struct winbindd_async_request *state, *tmp;
101 SMB_ASSERT(continuation != NULL);
103 state = TALLOC_P(mem_ctx, struct winbindd_async_request);
106 DEBUG(0, ("talloc failed\n"));
107 continuation(private_data, False);
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;
118 DLIST_ADD_END(child->requests, state, tmp);
120 schedule_async_request(child);
125 static void async_request_sent(void *private_data, BOOL success)
127 struct winbindd_async_request *state =
128 talloc_get_type_abort(private_data, struct winbindd_async_request);
131 DEBUG(5, ("Could not send async request\n"));
133 state->response->length = sizeof(struct winbindd_response);
134 state->response->result = WINBINDD_ERROR;
135 state->continuation(state->private_data, False);
139 /* Request successfully sent to the child, setup the wait for reply */
141 setup_async_read(&state->child->event,
142 &state->response->result,
143 sizeof(state->response->result),
144 async_reply_recv, state);
147 static void async_reply_recv(void *private_data, BOOL success)
149 struct winbindd_async_request *state =
150 talloc_get_type_abort(private_data, struct winbindd_async_request);
151 struct winbindd_child *child = state->child;
153 state->response->length = sizeof(struct winbindd_response);
156 DEBUG(5, ("Could not receive async reply\n"));
157 state->response->result = WINBINDD_ERROR;
161 if (state->response->result == WINBINDD_OK)
162 SMB_ASSERT(cache_retrieve_response(child->pid,
165 DLIST_REMOVE(child->requests, state);
167 schedule_async_request(child);
169 state->continuation(state->private_data, True);
172 static BOOL fork_domain_child(struct winbindd_child *child);
174 static void schedule_async_request(struct winbindd_child *child)
176 struct winbindd_async_request *request = child->requests;
178 if (request == NULL) {
182 if (child->event.flags != 0) {
186 if ((child->pid == 0) && (!fork_domain_child(child))) {
187 /* Cancel all outstanding requests */
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);
198 setup_async_write(&child->event, request->request,
199 sizeof(*request->request),
200 async_request_sent, request);
204 struct domain_request_state {
206 struct winbindd_domain *domain;
207 struct winbindd_request *request;
208 struct winbindd_response *response;
209 void (*continuation)(void *private_data, BOOL success);
213 static void domain_init_recv(void *private_data, BOOL success);
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),
222 struct domain_request_state *state;
224 if (domain->initialized) {
225 async_request(mem_ctx, &domain->child, request, response,
226 continuation, private_data);
230 state = TALLOC_P(mem_ctx, struct domain_request_state);
232 DEBUG(0, ("talloc failed\n"));
233 continuation(private_data, False);
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;
244 init_child_connection(domain, domain_init_recv, state);
247 static void recvfrom_child(void *private_data, BOOL success)
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;
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. */
257 state->response.result = WINBINDD_PENDING;
259 if ((!success) || (result != WINBINDD_OK)) {
260 request_error(state);
267 void sendto_child(struct winbindd_cli_state *state,
268 struct winbindd_child *child)
270 async_request(state->mem_ctx, child, &state->request,
271 &state->response, recvfrom_child, state);
274 void sendto_domain(struct winbindd_cli_state *state,
275 struct winbindd_domain *domain)
277 async_domain_request(state->mem_ctx, domain,
278 &state->request, &state->response,
279 recvfrom_child, state);
282 static void domain_init_recv(void *private_data, BOOL success)
284 struct domain_request_state *state =
285 talloc_get_type_abort(private_data, struct domain_request_state);
288 DEBUG(5, ("Domain init returned an error\n"));
289 state->continuation(state->private_data, False);
293 async_request(state->mem_ctx, &state->domain->child,
294 state->request, state->response,
295 state->continuation, state->private_data);
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;
305 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
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" },
330 { WINBINDD_NUM_CMDS, NULL, "NONE" }
333 static void child_process_request(struct winbindd_domain *domain,
334 struct winbindd_cli_state *state)
336 struct winbindd_child_dispatch_table *table;
338 /* Free response data - we may be interrupted and receive another
339 command before being able to send this data off. */
341 state->response.result = WINBINDD_ERROR;
342 state->response.length = sizeof(struct winbindd_response);
344 state->mem_ctx = talloc_init("winbind request");
345 if (state->mem_ctx == NULL)
348 /* Process command */
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);
360 DEBUG(10,("process_request: unknown request fn number %d\n",
361 (int)state->request.cmd ));
362 state->response.result = WINBINDD_ERROR;
365 talloc_destroy(state->mem_ctx);
368 void setup_domain_child(struct winbindd_domain *domain,
369 struct winbindd_child *child,
370 const char *explicit_logfile)
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);
379 smb_panic("Internal error: domain == NULL && "
380 "explicit_logfile == NULL");
383 child->domain = domain;
386 struct winbindd_child *children = NULL;
388 void winbind_child_died(pid_t pid)
390 struct winbindd_child *child;
392 for (child = children; child != NULL; child = child->next) {
393 if (child->pid == pid) {
399 DEBUG(0, ("Unknown child %d died!\n", pid));
403 remove_fd_event(&child->event);
404 close(child->event.fd);
406 child->event.flags = 0;
409 schedule_async_request(child);
412 static BOOL fork_domain_child(struct winbindd_child *child)
415 struct winbindd_cli_state state;
416 extern BOOL override_logfile;
418 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
419 DEBUG(0, ("Could not open child pipe: %s\n",
425 state.pid = getpid();
427 child->pid = sys_fork();
429 if (child->pid == -1) {
430 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
434 if (child->pid != 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);
448 state.sock = fdpair[0];
451 /* tdb needs special fork handling */
452 if (tdb_reopen_all() == -1) {
453 DEBUG(0,("tdb_reopen_all failed.\n"));
457 close_conns_after_fork();
459 if (!override_logfile) {
460 lp_set_logfile(child->logfilename);
465 /* free up any talloc memory */
467 main_loop_talloc_free();
469 /* fetch a request from the main daemon */
470 dual_client_read(&state);
472 if (state.finished) {
473 /* we lost contact with our parent */
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));
482 state.request.null_term = '\0';
483 child_process_request(child->domain, &state);
485 if (state.response.result == WINBINDD_OK)
486 cache_store_response(sys_getpid(),
489 SAFE_FREE(state.response.extra_data);
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... */
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"));
503 state.read_buf_len = 0;