X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fwinbindd%2Fwinbindd_dual.c;h=874d556a3151fe21d63f794842c2c7cf4543adee;hb=7f2d45a6c2a88dd8833fc66d314ec21507dd52c3;hp=4f314cf59f59ab5dd86a1c3cd06922d51f717765;hpb=fd3eeb3878c94bfb8d2134dbdc17cfb5279d088c;p=samba.git diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c index 4f314cf59f5..874d556a315 100644 --- a/source3/winbindd/winbindd_dual.c +++ b/source3/winbindd/winbindd_dual.c @@ -29,52 +29,56 @@ #include "includes.h" #include "winbindd.h" -#include "../../nsswitch/libwbclient/wbc_async.h" +#include "rpc_client/rpc_client.h" +#include "nsswitch/wb_reqtrans.h" +#include "secrets.h" +#include "../lib/util/select.h" +#include "../libcli/security/security.h" +#include "system/select.h" +#include "messages.h" +#include "../lib/util/tevent_unix.h" +#include "lib/param/loadparm.h" +#include "lib/util/sys_rw.h" +#include "lib/util/sys_rw_data.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND extern bool override_logfile; -extern struct winbindd_methods cache_methods; + +static struct winbindd_child *winbindd_children = NULL; /* Read some data from a client connection */ -static NTSTATUS child_read_request(struct winbindd_cli_state *state) +static NTSTATUS child_read_request(int sock, struct winbindd_request *wreq) { NTSTATUS status; - /* Read data */ - - status = read_data(state->sock, (char *)state->request, - sizeof(*state->request)); - + status = read_data_ntstatus(sock, (char *)wreq, sizeof(*wreq)); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("child_read_request: read_data failed: %s\n", nt_errstr(status))); return status; } - if (state->request->extra_len == 0) { - state->request->extra_data.data = NULL; + if (wreq->extra_len == 0) { + wreq->extra_data.data = NULL; return NT_STATUS_OK; } - DEBUG(10, ("Need to read %d extra bytes\n", (int)state->request->extra_len)); + DEBUG(10, ("Need to read %d extra bytes\n", (int)wreq->extra_len)); - state->request->extra_data.data = - SMB_MALLOC_ARRAY(char, state->request->extra_len + 1); - - if (state->request->extra_data.data == NULL) { + wreq->extra_data.data = SMB_MALLOC_ARRAY(char, wreq->extra_len + 1); + if (wreq->extra_data.data == NULL) { DEBUG(0, ("malloc failed\n")); return NT_STATUS_NO_MEMORY; } /* Ensure null termination */ - state->request->extra_data.data[state->request->extra_len] = '\0'; - - status= read_data(state->sock, state->request->extra_data.data, - state->request->extra_len); + wreq->extra_data.data[wreq->extra_len] = '\0'; + status = read_data_ntstatus(sock, wreq->extra_data.data, + wreq->extra_len); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Could not read extra data: %s\n", nt_errstr(status))); @@ -82,6 +86,31 @@ static NTSTATUS child_read_request(struct winbindd_cli_state *state) return status; } +static NTSTATUS child_write_response(int sock, struct winbindd_response *wrsp) +{ + struct iovec iov[2]; + int iov_count; + + iov[0].iov_base = (void *)wrsp; + iov[0].iov_len = sizeof(struct winbindd_response); + iov_count = 1; + + if (wrsp->length > sizeof(struct winbindd_response)) { + iov[1].iov_base = (void *)wrsp->extra_data.data; + iov[1].iov_len = wrsp->length-iov[0].iov_len; + iov_count = 2; + } + + DEBUG(10, ("Writing %d bytes to parent\n", (int)wrsp->length)); + + if (write_data_iov(sock, iov, iov_count) != wrsp->length) { + DEBUG(0, ("Could not write result\n")); + return NT_STATUS_INVALID_HANDLE; + } + + return NT_STATUS_OK; +} + /* * Do winbind child async request. This is not simply wb_simple_trans. We have * to do the queueing ourselves because while a request is queued, the child @@ -90,6 +119,8 @@ static NTSTATUS child_read_request(struct winbindd_cli_state *state) struct wb_child_request_state { struct tevent_context *ev; + struct tevent_req *queue_subreq; + struct tevent_req *subreq; struct winbindd_child *child; struct winbindd_request *request; struct winbindd_response *response; @@ -97,9 +128,12 @@ struct wb_child_request_state { static bool fork_domain_child(struct winbindd_child *child); -static void wb_child_request_trigger(struct tevent_req *req, - void *private_data); +static void wb_child_request_waited(struct tevent_req *subreq); static void wb_child_request_done(struct tevent_req *subreq); +static void wb_child_request_orphaned(struct tevent_req *subreq); + +static void wb_child_request_cleanup(struct tevent_req *req, + enum tevent_req_state req_state); struct tevent_req *wb_child_request_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -108,6 +142,7 @@ struct tevent_req *wb_child_request_send(TALLOC_CTX *mem_ctx, { struct tevent_req *req; struct wb_child_request_state *state; + struct tevent_req *subreq; req = tevent_req_create(mem_ctx, &state, struct wb_child_request_state); @@ -119,38 +154,51 @@ struct tevent_req *wb_child_request_send(TALLOC_CTX *mem_ctx, state->child = child; state->request = request; - if (!tevent_queue_add(child->queue, ev, req, - wb_child_request_trigger, NULL)) { - tevent_req_nomem(NULL, req); + subreq = tevent_queue_wait_send(state, ev, child->queue); + if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } + tevent_req_set_callback(subreq, wb_child_request_waited, req); + state->queue_subreq = subreq; + + tevent_req_set_cleanup_fn(req, wb_child_request_cleanup); + return req; } -static void wb_child_request_trigger(struct tevent_req *req, - void *private_data) +static void wb_child_request_waited(struct tevent_req *subreq) { + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); struct wb_child_request_state *state = tevent_req_data( req, struct wb_child_request_state); - struct tevent_req *subreq; + bool ok; - if ((state->child->pid == 0) && (!fork_domain_child(state->child))) { + ok = tevent_queue_wait_recv(subreq); + if (!ok) { + tevent_req_oom(req); + return; + } + /* + * We need to keep state->queue_subreq + * in order to block the queue. + */ + subreq = NULL; + + if ((state->child->sock == -1) && (!fork_domain_child(state->child))) { tevent_req_error(req, errno); return; } - subreq = wb_simple_trans_send(state, winbind_event_context(), NULL, + subreq = wb_simple_trans_send(state, server_event_context(), NULL, state->child->sock, state->request); if (tevent_req_nomem(subreq, req)) { return; } - tevent_req_set_callback(subreq, wb_child_request_done, req); - if (!tevent_req_set_endtime(req, state->ev, - timeval_current_ofs(300, 0))) { - tevent_req_nomem(NULL, req); - return; - } + state->subreq = subreq; + tevent_req_set_callback(subreq, wb_child_request_done, req); + tevent_req_set_endtime(req, state->ev, timeval_current_ofs(300, 0)); } static void wb_child_request_done(struct tevent_req *subreq) @@ -162,7 +210,10 @@ static void wb_child_request_done(struct tevent_req *subreq) int ret, err; ret = wb_simple_trans_recv(subreq, state, &state->response, &err); - TALLOC_FREE(subreq); + /* Freeing the subrequest is deferred until the cleanup function, + * which has to know whether a subrequest exists, and consequently + * decide whether to shut down the pipe to the child process. + */ if (ret == -1) { tevent_req_error(req, err); return; @@ -170,6 +221,25 @@ static void wb_child_request_done(struct tevent_req *subreq) tevent_req_done(req); } +static void wb_child_request_orphaned(struct tevent_req *subreq) +{ + struct winbindd_child *child = + (struct winbindd_child *)tevent_req_callback_data_void(subreq); + + DBG_WARNING("cleanup orphaned subreq[%p]\n", subreq); + TALLOC_FREE(subreq); + + if (child->domain != NULL) { + /* + * If the child is attached to a domain, + * we need to make sure the domain queue + * can move forward, after the orphaned + * request is done. + */ + tevent_queue_start(child->domain->queue); + } +} + int wb_child_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct winbindd_response **presponse, int *err) { @@ -183,14 +253,131 @@ int wb_child_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, return 0; } +static void wb_child_request_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + struct wb_child_request_state *state = + tevent_req_data(req, struct wb_child_request_state); + + if (state->subreq == NULL) { + /* nothing to cleanup */ + return; + } + + if (req_state == TEVENT_REQ_RECEIVED) { + struct tevent_req *subreq = NULL; + + /* + * Our caller gave up, but we need to keep + * the low level request (wb_simple_trans) + * in order to maintain the parent child protocol. + * + * We also need to keep the child queue blocked + * until we got the response from the child. + */ + + subreq = talloc_move(state->child->queue, &state->subreq); + talloc_move(subreq, &state->queue_subreq); + tevent_req_set_callback(subreq, + wb_child_request_orphaned, + state->child); + + DBG_WARNING("keep orphaned subreq[%p]\n", subreq); + return; + } + + TALLOC_FREE(state->subreq); + TALLOC_FREE(state->queue_subreq); + + if (state->child->domain != NULL) { + /* + * If the child is attached to a domain, + * we need to make sure the domain queue + * can move forward, after the request + * is done. + */ + tevent_queue_start(state->child->domain->queue); + } + + if (req_state == TEVENT_REQ_DONE) { + /* transmitted request and got response */ + return; + } + + /* + * Failed to transmit and receive response, or request + * cancelled while being serviced. + * The basic parent/child communication broke, close + * our socket + */ + close(state->child->sock); + state->child->sock = -1; + DLIST_REMOVE(winbindd_children, state->child); +} + +static struct winbindd_child *choose_domain_child(struct winbindd_domain *domain) +{ + struct winbindd_child *shortest = &domain->children[0]; + struct winbindd_child *current; + int i; + + for (i=0; ichildren[i]; + current_len = tevent_queue_length(current->queue); + + if (current_len == 0) { + /* idle child */ + return current; + } + + shortest_len = tevent_queue_length(shortest->queue); + + if (current_len < shortest_len) { + shortest = current; + } + } + + return shortest; +} + +struct dcerpc_binding_handle *dom_child_handle(struct winbindd_domain *domain) +{ + return domain->binding_handle; +} + struct wb_domain_request_state { struct tevent_context *ev; + struct tevent_queue_entry *queue_entry; struct winbindd_domain *domain; + struct winbindd_child *child; struct winbindd_request *request; struct winbindd_request *init_req; struct winbindd_response *response; + struct tevent_req *pending_subreq; }; +static void wb_domain_request_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + struct wb_domain_request_state *state = tevent_req_data( + req, struct wb_domain_request_state); + + /* + * If we're completely done or got a failure. + * we should remove ourself from the domain queue, + * after removing the child subreq from the child queue + * and give the next one in the queue the chance + * to check for an idle child. + */ + TALLOC_FREE(state->pending_subreq); + TALLOC_FREE(state->queue_entry); + tevent_queue_start(state->domain->queue); +} + +static void wb_domain_request_trigger(struct tevent_req *req, + void *private_data); static void wb_domain_request_gotdc(struct tevent_req *subreq); static void wb_domain_request_initialized(struct tevent_req *subreq); static void wb_domain_request_done(struct tevent_req *subreq); @@ -200,7 +387,7 @@ struct tevent_req *wb_domain_request_send(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, struct winbindd_request *request) { - struct tevent_req *req, *subreq; + struct tevent_req *req; struct wb_domain_request_state *state; req = tevent_req_create(mem_ctx, &state, @@ -209,60 +396,109 @@ struct tevent_req *wb_domain_request_send(TALLOC_CTX *mem_ctx, return NULL; } + state->domain = domain; + state->ev = ev; + state->request = request; + + tevent_req_set_cleanup_fn(req, wb_domain_request_cleanup); + + state->queue_entry = tevent_queue_add_entry( + domain->queue, state->ev, req, + wb_domain_request_trigger, NULL); + if (tevent_req_nomem(state->queue_entry, req)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void wb_domain_request_trigger(struct tevent_req *req, + void *private_data) +{ + struct wb_domain_request_state *state = tevent_req_data( + req, struct wb_domain_request_state); + struct winbindd_domain *domain = state->domain; + struct tevent_req *subreq = NULL; + size_t shortest_queue_length; + + state->child = choose_domain_child(domain); + shortest_queue_length = tevent_queue_length(state->child->queue); + if (shortest_queue_length > 0) { + /* + * All children are busy, we need to stop + * the queue and untrigger our own queue + * entry. Once a pending request + * is done it calls tevent_queue_start + * and we get retriggered. + */ + state->child = NULL; + tevent_queue_stop(state->domain->queue); + tevent_queue_entry_untrigger(state->queue_entry); + return; + } + if (domain->initialized) { - subreq = wb_child_request_send(state, ev, &domain->child, - request); + subreq = wb_child_request_send(state, state->ev, state->child, + state->request); if (tevent_req_nomem(subreq, req)) { - return tevent_req_post(req, ev); + return; } tevent_req_set_callback(subreq, wb_domain_request_done, req); - return req; - } + state->pending_subreq = subreq; - state->domain = domain; - state->ev = ev; - state->request = request; + /* + * Once the domain is initialized and + * once we placed our real request into the child queue, + * we can remove ourself from the domain queue + * and give the next one in the queue the chance + * to check for an idle child. + */ + TALLOC_FREE(state->queue_entry); + return; + } state->init_req = talloc_zero(state, struct winbindd_request); if (tevent_req_nomem(state->init_req, req)) { - return tevent_req_post(req, ev); + return; } if (IS_DC || domain->primary || domain->internal) { /* The primary domain has to find the DC name itself */ state->init_req->cmd = WINBINDD_INIT_CONNECTION; fstrcpy(state->init_req->domain_name, domain->name); - state->init_req->data.init_conn.is_primary = - domain->primary ? true : false; + state->init_req->data.init_conn.is_primary = domain->primary; fstrcpy(state->init_req->data.init_conn.dcname, ""); - subreq = wb_child_request_send(state, ev, &domain->child, + subreq = wb_child_request_send(state, state->ev, state->child, state->init_req); if (tevent_req_nomem(subreq, req)) { - return tevent_req_post(req, ev); + return; } tevent_req_set_callback(subreq, wb_domain_request_initialized, req); - return req; + state->pending_subreq = subreq; + return; } /* - * Ask our DC for a DC name + * This is *not* the primary domain, + * let's ask our DC about a DC name. + * + * We prefer getting a dns name in dc_unc, + * which is indicated by DS_RETURN_DNS_NAME. + * For NT4 domains we still get the netbios name. */ - domain = find_our_domain(); - - /* This is *not* the primary domain, let's ask our DC about a DC - * name */ - - state->init_req->cmd = WINBINDD_GETDCNAME; - fstrcpy(state->init_req->domain_name, domain->name); - - subreq = wb_child_request_send(state, ev, &domain->child, request); + subreq = wb_dsgetdcname_send(state, state->ev, + state->domain->name, + NULL, /* domain_guid */ + NULL, /* site_name */ + DS_RETURN_DNS_NAME); /* flags */ if (tevent_req_nomem(subreq, req)) { - return tevent_req_post(req, ev); + return; } tevent_req_set_callback(subreq, wb_domain_request_gotdc, req); - return req; + state->pending_subreq = subreq; + return; } static void wb_domain_request_gotdc(struct tevent_req *subreq) @@ -271,29 +507,36 @@ static void wb_domain_request_gotdc(struct tevent_req *subreq) subreq, struct tevent_req); struct wb_domain_request_state *state = tevent_req_data( req, struct wb_domain_request_state); - struct winbindd_response *response; - int ret, err; + struct netr_DsRGetDCNameInfo *dcinfo = NULL; + NTSTATUS status; + const char *dcname = NULL; - ret = wb_child_request_recv(subreq, talloc_tos(), &response, &err); + state->pending_subreq = NULL; + + status = wb_dsgetdcname_recv(subreq, state, &dcinfo); TALLOC_FREE(subreq); - if (ret == -1) { - tevent_req_error(req, err); + if (tevent_req_nterror(req, status)) { return; } + dcname = dcinfo->dc_unc; + while (dcname != NULL && *dcname == '\\') { + dcname++; + } state->init_req->cmd = WINBINDD_INIT_CONNECTION; fstrcpy(state->init_req->domain_name, state->domain->name); state->init_req->data.init_conn.is_primary = False; fstrcpy(state->init_req->data.init_conn.dcname, - response->data.dc_name); + dcname); - TALLOC_FREE(response); + TALLOC_FREE(dcinfo); - subreq = wb_child_request_send(state, state->ev, &state->domain->child, + subreq = wb_child_request_send(state, state->ev, state->child, state->init_req); if (tevent_req_nomem(subreq, req)) { return; } tevent_req_set_callback(subreq, wb_domain_request_initialized, req); + state->pending_subreq = subreq; } static void wb_domain_request_initialized(struct tevent_req *subreq) @@ -305,6 +548,8 @@ static void wb_domain_request_initialized(struct tevent_req *subreq) struct winbindd_response *response; int ret, err; + state->pending_subreq = NULL; + ret = wb_child_request_recv(subreq, talloc_tos(), &response, &err); TALLOC_FREE(subreq); if (ret == -1) { @@ -319,8 +564,26 @@ static void wb_domain_request_initialized(struct tevent_req *subreq) tevent_req_error(req, EINVAL); return; } - fstrcpy(state->domain->name, response->data.domain_info.name); - fstrcpy(state->domain->alt_name, response->data.domain_info.alt_name); + + talloc_free(state->domain->name); + state->domain->name = talloc_strdup(state->domain, + response->data.domain_info.name); + if (state->domain->name == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + if (response->data.domain_info.alt_name[0] != '\0') { + talloc_free(state->domain->alt_name); + + state->domain->alt_name = talloc_strdup(state->domain, + response->data.domain_info.alt_name); + if (state->domain->alt_name == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + } + state->domain->native_mode = response->data.domain_info.native_mode; state->domain->active_directory = response->data.domain_info.active_directory; @@ -328,12 +591,22 @@ static void wb_domain_request_initialized(struct tevent_req *subreq) TALLOC_FREE(response); - subreq = wb_child_request_send(state, state->ev, &state->domain->child, + subreq = wb_child_request_send(state, state->ev, state->child, state->request); if (tevent_req_nomem(subreq, req)) { return; } tevent_req_set_callback(subreq, wb_domain_request_done, req); + state->pending_subreq = subreq; + + /* + * Once the domain is initialized and + * once we placed our real request into the child queue, + * we can remove ourself from the domain queue + * and give the next one in the queue the chance + * to check for an idle child. + */ + TALLOC_FREE(state->queue_entry); } static void wb_domain_request_done(struct tevent_req *subreq) @@ -344,6 +617,8 @@ static void wb_domain_request_done(struct tevent_req *subreq) req, struct wb_domain_request_state); int ret, err; + state->pending_subreq = NULL; + ret = wb_child_request_recv(subreq, talloc_tos(), &state->response, &err); TALLOC_FREE(subreq); @@ -393,7 +668,7 @@ static void child_process_request(struct winbindd_child *child, } } - DEBUG(1 ,("child_process_request: unknown request fn number %d\n", + DEBUG(1, ("child_process_request: unknown request fn number %d\n", (int)state->request->cmd)); state->response->result = WINBINDD_ERROR; } @@ -404,30 +679,52 @@ void setup_child(struct winbindd_domain *domain, struct winbindd_child *child, const char *logname) { if (logprefix && logname) { + char *logbase = NULL; + + if (*lp_logfile(talloc_tos())) { + char *end = NULL; + + if (asprintf(&logbase, "%s", lp_logfile(talloc_tos())) < 0) { + smb_panic("Internal error: asprintf failed"); + } + + if ((end = strrchr_m(logbase, '/'))) { + *end = '\0'; + } + } else { + if (asprintf(&logbase, "%s", get_dyn_LOGFILEBASE()) < 0) { + smb_panic("Internal error: asprintf failed"); + } + } + if (asprintf(&child->logfilename, "%s/%s-%s", - get_dyn_LOGFILEBASE(), logprefix, logname) < 0) { + logbase, logprefix, logname) < 0) { + SAFE_FREE(logbase); smb_panic("Internal error: asprintf failed"); } + + SAFE_FREE(logbase); } else { smb_panic("Internal error: logprefix == NULL && " "logname == NULL"); } + child->sock = -1; child->domain = domain; child->table = table; child->queue = tevent_queue_create(NULL, "winbind_child"); SMB_ASSERT(child->queue != NULL); - child->rpccli = wbint_rpccli_create(NULL, domain, child); - SMB_ASSERT(child->rpccli != NULL); + if (domain == NULL) { + child->binding_handle = wbint_binding_handle(NULL, NULL, child); + SMB_ASSERT(child->binding_handle != NULL); + } } -struct winbindd_child *children = NULL; - void winbind_child_died(pid_t pid) { struct winbindd_child *child; - for (child = children; child != NULL; child = child->next) { + for (child = winbindd_children; child != NULL; child = child->next) { if (child->pid == pid) { break; } @@ -440,11 +737,13 @@ void winbind_child_died(pid_t pid) /* This will be re-added in fork_domain_child() */ - DLIST_REMOVE(children, child); - - close(child->sock); - child->sock = -1; + DLIST_REMOVE(winbindd_children, child); child->pid = 0; + + if (child->sock != -1) { + close(child->sock); + child->sock = -1; + } } /* Ensure any negative cache entries with the netbios or realm names are removed. */ @@ -452,7 +751,7 @@ void winbind_child_died(pid_t pid) void winbindd_flush_negative_conn_cache(struct winbindd_domain *domain) { flush_negative_conn_cache_for_domain(domain->name); - if (*domain->alt_name) { + if (domain->alt_name != NULL) { flush_negative_conn_cache_for_domain(domain->alt_name); } } @@ -475,7 +774,7 @@ void winbind_msg_debug(struct messaging_context *msg_ctx, debug_message(msg_ctx, private_data, MSG_DEBUG, server_id, data); - for (child = children; child != NULL; child = child->next) { + for (child = winbindd_children; child != NULL; child = child->next) { DEBUG(10,("winbind_msg_debug: sending message to pid %u.\n", (unsigned int)child->pid)); @@ -520,7 +819,7 @@ void winbind_msg_offline(struct messaging_context *msg_ctx, set_domain_offline(domain); } - for (child = children; child != NULL; child = child->next) { + for (child = winbindd_children; child != NULL; child = child->next) { /* Don't send message to internal children. We've already done so above. */ if (!child->domain || winbindd_internal_child(child)) { @@ -536,11 +835,11 @@ void winbind_msg_offline(struct messaging_context *msg_ctx, we only set it online / offline for that domain. */ DEBUG(10,("winbind_msg_offline: sending message to pid %u for domain %s.\n", - (unsigned int)child->pid, domain->name )); + (unsigned int)child->pid, child->domain->name )); messaging_send_buf(msg_ctx, pid_to_procid(child->pid), MSG_WINBIND_OFFLINE, - (uint8 *)child->domain->name, + (const uint8_t *)child->domain->name, strlen(child->domain->name)+1); } } @@ -589,13 +888,13 @@ void winbind_msg_online(struct messaging_context *msg_ctx, messaging_send_buf(msg_ctx, pid_to_procid(idmap->pid), MSG_WINBIND_ONLINE, - (uint8 *)domain->name, + (const uint8_t *)domain->name, strlen(domain->name)+1); } } } - for (child = children; child != NULL; child = child->next) { + for (child = winbindd_children; child != NULL; child = child->next) { /* Don't send message to internal childs. */ if (!child->domain || winbindd_internal_child(child)) { continue; @@ -614,7 +913,7 @@ void winbind_msg_online(struct messaging_context *msg_ctx, messaging_send_buf(msg_ctx, pid_to_procid(child->pid), MSG_WINBIND_ONLINE, - (uint8 *)child->domain->name, + (const uint8_t *)child->domain->name, strlen(child->domain->name)+1); } } @@ -654,16 +953,9 @@ void winbind_msg_onlinestatus(struct messaging_context *msg_ctx, { TALLOC_CTX *mem_ctx; const char *message; - struct server_id *sender; DEBUG(5,("winbind_msg_onlinestatus received.\n")); - if (!data->data) { - return; - } - - sender = (struct server_id *)data->data; - mem_ctx = talloc_init("winbind_msg_onlinestatus"); if (mem_ctx == NULL) { return; @@ -675,8 +967,8 @@ void winbind_msg_onlinestatus(struct messaging_context *msg_ctx, return; } - messaging_send_buf(msg_ctx, *sender, MSG_WINBIND_ONLINESTATUS, - (uint8 *)message, strlen(message) + 1); + messaging_send_buf(msg_ctx, server_id, MSG_WINBIND_ONLINESTATUS, + (const uint8_t *)message, strlen(message) + 1); talloc_destroy(mem_ctx); } @@ -691,9 +983,9 @@ void winbind_msg_dump_event_list(struct messaging_context *msg_ctx, DEBUG(10,("winbind_msg_dump_event_list received\n")); - dump_event_list(winbind_event_context()); + DBG_WARNING("dump event list no longer implemented\n"); - for (child = children; child != NULL; child = child->next) { + for (child = winbindd_children; child != NULL; child = child->next) { DEBUG(10,("winbind_msg_dump_event_list: sending message to pid %u\n", (unsigned int)child->pid)); @@ -713,7 +1005,6 @@ void winbind_msg_dump_domain_list(struct messaging_context *msg_ctx, { TALLOC_CTX *mem_ctx; const char *message = NULL; - struct server_id *sender = NULL; const char *domain = NULL; char *s = NULL; NTSTATUS status; @@ -721,22 +1012,13 @@ void winbind_msg_dump_domain_list(struct messaging_context *msg_ctx, DEBUG(5,("winbind_msg_dump_domain_list received.\n")); - if (!data || !data->data) { - return; - } - - if (data->length < sizeof(struct server_id)) { - return; - } - mem_ctx = talloc_init("winbind_msg_dump_domain_list"); if (!mem_ctx) { return; } - sender = (struct server_id *)data->data; - if (data->length > sizeof(struct server_id)) { - domain = (const char *)data->data+sizeof(struct server_id); + if (data->length > 0) { + domain = (const char *)data->data; } if (domain) { @@ -751,9 +1033,9 @@ void winbind_msg_dump_domain_list(struct messaging_context *msg_ctx, return; } - messaging_send_buf(msg_ctx, *sender, + messaging_send_buf(msg_ctx, server_id, MSG_WINBIND_DUMP_DOMAIN_LIST, - (uint8_t *)message, strlen(message) + 1); + (const uint8_t *)message, strlen(message) + 1); talloc_destroy(mem_ctx); @@ -776,7 +1058,7 @@ void winbind_msg_dump_domain_list(struct messaging_context *msg_ctx, } } - status = messaging_send_buf(msg_ctx, *sender, + status = messaging_send_buf(msg_ctx, server_id, MSG_WINBIND_DUMP_DOMAIN_LIST, (uint8_t *)s, strlen(s) + 1); if (!NT_STATUS_IS_OK(status)) { @@ -787,15 +1069,14 @@ void winbind_msg_dump_domain_list(struct messaging_context *msg_ctx, talloc_destroy(mem_ctx); } -static void account_lockout_policy_handler(struct event_context *ctx, - struct timed_event *te, +static void account_lockout_policy_handler(struct tevent_context *ctx, + struct tevent_timer *te, struct timeval now, void *private_data) { struct winbindd_child *child = (struct winbindd_child *)private_data; TALLOC_CTX *mem_ctx = NULL; - struct winbindd_methods *methods; struct samr_DomInfo12 lockout_policy; NTSTATUS result; @@ -811,13 +1092,12 @@ static void account_lockout_policy_handler(struct event_context *ctx, return; } - methods = child->domain->methods; - mem_ctx = talloc_init("account_lockout_policy_handler ctx"); if (!mem_ctx) { result = NT_STATUS_NO_MEMORY; } else { - result = methods->lockout_policy(child->domain, mem_ctx, &lockout_policy); + result = wb_cache_lockout_policy(child->domain, mem_ctx, + &lockout_policy); } TALLOC_FREE(mem_ctx); @@ -826,7 +1106,7 @@ static void account_lockout_policy_handler(struct event_context *ctx, nt_errstr(result))); } - child->lockout_policy_event = event_add_timed(winbind_event_context(), NULL, + child->lockout_policy_event = tevent_add_timer(server_event_context(), NULL, timeval_current_ofs(3600, 0), account_lockout_policy_handler, child); @@ -904,15 +1184,16 @@ static bool calculate_next_machine_pwd_change(const char *domain, return true; } -static void machine_password_change_handler(struct event_context *ctx, - struct timed_event *te, +static void machine_password_change_handler(struct tevent_context *ctx, + struct tevent_timer *te, struct timeval now, void *private_data) { + struct messaging_context *msg_ctx = server_messaging_context(); struct winbindd_child *child = (struct winbindd_child *)private_data; struct rpc_pipe_client *netlogon_pipe = NULL; - TALLOC_CTX *frame; + struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL; NTSTATUS result; struct timeval next_change; @@ -941,7 +1222,9 @@ static void machine_password_change_handler(struct event_context *ctx, return; } - result = cm_connect_netlogon(child->domain, &netlogon_pipe); + result = cm_connect_netlogon_secure(child->domain, + &netlogon_pipe, + &netlogon_creds_ctx); if (!NT_STATUS_IS_OK(result)) { DEBUG(10,("machine_password_change_handler: " "failed to connect netlogon pipe: %s\n", @@ -949,15 +1232,15 @@ static void machine_password_change_handler(struct event_context *ctx, return; } - frame = talloc_stackframe(); - - result = trust_pw_find_change_and_store_it(netlogon_pipe, - frame, - child->domain->name); - TALLOC_FREE(frame); + result = trust_pw_change(netlogon_creds_ctx, + msg_ctx, + netlogon_pipe->binding_handle, + child->domain->name, + child->domain->dcname, + false); /* force */ DEBUG(10, ("machine_password_change_handler: " - "trust_pw_find_change_and_store_it returned %s\n", + "trust_pw_change returned %s\n", nt_errstr(result))); if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { @@ -966,7 +1249,7 @@ static void machine_password_change_handler(struct event_context *ctx, "password was changed and we didn't know it. " "Killing connections to domain %s\n", child->domain->name)); - TALLOC_FREE(child->domain->conn.netlogon_pipe); + invalidate_cm_connection(child->domain); } if (!calculate_next_machine_pwd_change(child->domain->name, @@ -988,7 +1271,7 @@ static void machine_password_change_handler(struct event_context *ctx, } done: - child->machine_password_change_event = event_add_timed(winbind_event_context(), NULL, + child->machine_password_change_event = tevent_add_timer(server_event_context(), NULL, next_change, machine_password_change_handler, child); @@ -1097,20 +1380,23 @@ static void child_msg_dump_event_list(struct messaging_context *msg, DATA_BLOB *data) { DEBUG(5,("child_msg_dump_event_list received\n")); - - dump_event_list(winbind_event_context()); + DBG_WARNING("dump_event_list no longer implemented\n"); } -bool winbindd_reinit_after_fork(const char *logfilename) +NTSTATUS winbindd_reinit_after_fork(const struct winbindd_child *myself, + const char *logfilename) { struct winbindd_domain *domain; struct winbindd_child *cl; + NTSTATUS status; - if (!NT_STATUS_IS_OK(reinit_after_fork(winbind_messaging_context(), - winbind_event_context(), - true))) { + status = reinit_after_fork( + server_messaging_context(), + server_event_context(), + true, NULL); + if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("reinit_after_fork() failed\n")); - return false; + return status; } close_conns_after_fork(); @@ -1121,32 +1407,37 @@ bool winbindd_reinit_after_fork(const char *logfilename) } if (!winbindd_setup_sig_term_handler(false)) - return false; + return NT_STATUS_NO_MEMORY; if (!winbindd_setup_sig_hup_handler(override_logfile ? NULL : logfilename)) - return false; + return NT_STATUS_NO_MEMORY; /* Stop zombies in children */ CatchChild(); /* Don't handle the same messages as our parent. */ - messaging_deregister(winbind_messaging_context(), + messaging_deregister(server_messaging_context(), MSG_SMB_CONF_UPDATED, NULL); - messaging_deregister(winbind_messaging_context(), + messaging_deregister(server_messaging_context(), MSG_SHUTDOWN, NULL); - messaging_deregister(winbind_messaging_context(), + messaging_deregister(server_messaging_context(), MSG_WINBIND_OFFLINE, NULL); - messaging_deregister(winbind_messaging_context(), + messaging_deregister(server_messaging_context(), MSG_WINBIND_ONLINE, NULL); - messaging_deregister(winbind_messaging_context(), + messaging_deregister(server_messaging_context(), MSG_WINBIND_ONLINESTATUS, NULL); - messaging_deregister(winbind_messaging_context(), + messaging_deregister(server_messaging_context(), MSG_DUMP_EVENT_LIST, NULL); - messaging_deregister(winbind_messaging_context(), + messaging_deregister(server_messaging_context(), MSG_WINBIND_DUMP_DOMAIN_LIST, NULL); - messaging_deregister(winbind_messaging_context(), + messaging_deregister(server_messaging_context(), MSG_DEBUG, NULL); + messaging_deregister(server_messaging_context(), + MSG_WINBIND_DOMAIN_OFFLINE, NULL); + messaging_deregister(server_messaging_context(), + MSG_WINBIND_DOMAIN_ONLINE, NULL); + /* We have destroyed all events in the winbindd_event_context * in reinit_after_fork(), so clean out all possible pending * event pointers. */ @@ -1163,7 +1454,7 @@ bool winbindd_reinit_after_fork(const char *logfilename) ccache_remove_all_after_fork(); /* Destroy all possible events in child list. */ - for (cl = children; cl != NULL; cl = cl->next) { + for (cl = winbindd_children; cl != NULL; cl = cl->next) { TALLOC_FREE(cl->lockout_policy_event); TALLOC_FREE(cl->machine_password_change_event); @@ -1172,6 +1463,14 @@ bool winbindd_reinit_after_fork(const char *logfilename) * go through the parent. */ cl->pid = (pid_t)0; + + /* + * Close service sockets to all other children + */ + if ((cl != myself) && (cl->sock != -1)) { + close(cl->sock); + cl->sock = -1; + } } /* * This is a little tricky, children must not @@ -1192,7 +1491,7 @@ bool winbindd_reinit_after_fork(const char *logfilename) cl = idmap_child(); cl->pid = (pid_t)0; - return true; + return NT_STATUS_OK; } /* @@ -1205,13 +1504,55 @@ struct winbindd_domain *wb_child_domain(void) return child_domain; } +struct child_handler_state { + struct winbindd_child *child; + struct winbindd_cli_state cli; +}; + +static void child_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct child_handler_state *state = + (struct child_handler_state *)private_data; + NTSTATUS status; + + /* fetch a request from the main daemon */ + status = child_read_request(state->cli.sock, state->cli.request); + + if (!NT_STATUS_IS_OK(status)) { + /* we lost contact with our parent */ + _exit(0); + } + + DEBUG(4,("child daemon request %d\n", + (int)state->cli.request->cmd)); + + ZERO_STRUCTP(state->cli.response); + state->cli.request->null_term = '\0'; + state->cli.mem_ctx = talloc_tos(); + child_process_request(state->child, &state->cli); + + DEBUG(4, ("Finished processing child request %d\n", + (int)state->cli.request->cmd)); + + SAFE_FREE(state->cli.request->extra_data.data); + + status = child_write_response(state->cli.sock, state->cli.response); + if (!NT_STATUS_IS_OK(status)) { + exit(1); + } +} + static bool fork_domain_child(struct winbindd_child *child) { int fdpair[2]; - struct winbindd_cli_state state; + struct child_handler_state state; struct winbindd_request request; struct winbindd_response response; struct winbindd_domain *primary_domain = NULL; + NTSTATUS status; + ssize_t nwritten; + struct tevent_fd *fde; if (child->domain) { DEBUG(10, ("fork_domain_child called for domain '%s'\n", @@ -1227,22 +1568,43 @@ static bool fork_domain_child(struct winbindd_child *child) } ZERO_STRUCT(state); - state.pid = sys_getpid(); - state.request = &request; - state.response = &response; + state.child = child; + state.cli.pid = getpid(); + state.cli.request = &request; + state.cli.response = &response; - child->pid = sys_fork(); + child->pid = fork(); if (child->pid == -1) { DEBUG(0, ("Could not fork: %s\n", strerror(errno))); + close(fdpair[0]); + close(fdpair[1]); return False; } if (child->pid != 0) { /* Parent */ + ssize_t nread; + close(fdpair[0]); + + nread = sys_read(fdpair[1], &status, sizeof(status)); + if (nread != sizeof(status)) { + DEBUG(1, ("fork_domain_child: Could not read child status: " + "nread=%d, error=%s\n", (int)nread, + strerror(errno))); + close(fdpair[1]); + return false; + } + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("fork_domain_child: Child status is %s\n", + nt_errstr(status))); + close(fdpair[1]); + return false; + } + child->next = child->prev = NULL; - DLIST_ADD(children, child); + DLIST_ADD(winbindd_children, child); child->sock = fdpair[1]; return True; } @@ -1250,24 +1612,45 @@ static bool fork_domain_child(struct winbindd_child *child) /* Child */ child_domain = child->domain; - DEBUG(10, ("Child process %d\n", (int)sys_getpid())); + DEBUG(10, ("Child process %d\n", (int)getpid())); - state.sock = fdpair[0]; + state.cli.sock = fdpair[0]; close(fdpair[1]); - if (!winbindd_reinit_after_fork(child->logfilename)) { + status = winbindd_reinit_after_fork(child, child->logfilename); + + nwritten = sys_write(state.cli.sock, &status, sizeof(status)); + if (nwritten != sizeof(status)) { + DEBUG(1, ("fork_domain_child: Could not write status: " + "nwritten=%d, error=%s\n", (int)nwritten, + strerror(errno))); _exit(0); } + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("winbindd_reinit_after_fork failed: %s\n", + nt_errstr(status))); + _exit(0); + } + + if (child_domain != NULL) { + setproctitle("domain child [%s]", child_domain->name); + } else if (child == idmap_child()) { + setproctitle("idmap child"); + } /* Handle online/offline messages. */ - messaging_register(winbind_messaging_context(), NULL, + messaging_register(server_messaging_context(), NULL, MSG_WINBIND_OFFLINE, child_msg_offline); - messaging_register(winbind_messaging_context(), NULL, + messaging_register(server_messaging_context(), NULL, MSG_WINBIND_ONLINE, child_msg_online); - messaging_register(winbind_messaging_context(), NULL, + messaging_register(server_messaging_context(), NULL, MSG_DUMP_EVENT_LIST, child_msg_dump_event_list); - messaging_register(winbind_messaging_context(), NULL, + messaging_register(server_messaging_context(), NULL, MSG_DEBUG, debug_message); + messaging_register(server_messaging_context(), NULL, + MSG_WINBIND_IP_DROPPED, + winbind_msg_ip_dropped); + primary_domain = find_our_domain(); @@ -1279,7 +1662,7 @@ static bool fork_domain_child(struct winbindd_child *child) * try to bring domain online after fork. */ if ( child->domain ) { child->domain->startup = True; - child->domain->startup_time = time(NULL); + child->domain->startup_time = time_mono(NULL); /* we can be in primary domain or in trusted domain * If we are in trusted domain, set the primary domain * in start-up mode */ @@ -1287,7 +1670,7 @@ static bool fork_domain_child(struct winbindd_child *child) set_domain_online_request(child->domain); if (!(child->domain->primary)) { primary_domain->startup = True; - primary_domain->startup_time = time(NULL); + primary_domain->startup_time = time_mono(NULL); set_domain_online_request(primary_domain); } } @@ -1319,8 +1702,8 @@ static bool fork_domain_child(struct winbindd_child *child) set_domain_online_request(primary_domain); } - child->lockout_policy_event = event_add_timed( - winbind_event_context(), NULL, timeval_zero(), + child->lockout_policy_event = tevent_add_timer( + server_event_context(), NULL, timeval_zero(), account_lockout_policy_handler, child); } @@ -1333,129 +1716,58 @@ static bool fork_domain_child(struct winbindd_child *child) if (calculate_next_machine_pwd_change(child->domain->name, &next_change)) { - child->machine_password_change_event = event_add_timed( - winbind_event_context(), NULL, next_change, + child->machine_password_change_event = tevent_add_timer( + server_event_context(), NULL, next_change, machine_password_change_handler, child); } } + fde = tevent_add_fd(server_event_context(), NULL, state.cli.sock, + TEVENT_FD_READ, child_handler, &state); + if (fde == NULL) { + DEBUG(1, ("tevent_add_fd failed\n")); + _exit(1); + } + while (1) { int ret; - fd_set r_fds; - fd_set w_fds; - int maxfd; - struct timeval t; - struct timeval *tp; - struct timeval now; TALLOC_CTX *frame = talloc_stackframe(); - struct iovec iov[2]; - int iov_count; - NTSTATUS status; - if (run_events(winbind_event_context(), 0, NULL, NULL)) { - TALLOC_FREE(frame); - continue; + ret = tevent_loop_once(server_event_context()); + if (ret != 0) { + DEBUG(1, ("tevent_loop_once failed: %s\n", + strerror(errno))); + _exit(1); } - GetTimeOfDay(&now); - if (child->domain && child->domain->startup && - (now.tv_sec > child->domain->startup_time + 30)) { + (time_mono(NULL) > child->domain->startup_time + 30)) { /* No longer in "startup" mode. */ DEBUG(10,("fork_domain_child: domain %s no longer in 'startup' mode.\n", child->domain->name )); child->domain->startup = False; } - FD_ZERO(&r_fds); - FD_ZERO(&w_fds); - FD_SET(state.sock, &r_fds); - maxfd = state.sock; - - /* - * Initialize this high as event_add_to_select_args() - * uses a timeval_min() on this and next_event. Fix - * from Roel van Meer . - */ - t.tv_sec = 999999; - t.tv_usec = 0; - - event_add_to_select_args(winbind_event_context(), &now, - &r_fds, &w_fds, &t, &maxfd); - tp = get_timed_events_timeout(winbind_event_context(), &t); - if (tp) { - DEBUG(11,("select will use timeout of %u.%u seconds\n", - (unsigned int)tp->tv_sec, (unsigned int)tp->tv_usec )); - } - - ret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, tp); - - if (run_events(winbind_event_context(), ret, &r_fds, &w_fds)) { - /* We got a signal - continue. */ - TALLOC_FREE(frame); - continue; - } - - if (ret == 0) { - DEBUG(11,("nothing is ready yet, continue\n")); - TALLOC_FREE(frame); - continue; - } - - if (ret == -1 && errno == EINTR) { - /* We got a signal - continue. */ - TALLOC_FREE(frame); - continue; - } - - if (ret == -1 && errno != EINTR) { - DEBUG(0,("select error occured\n")); - TALLOC_FREE(frame); - perror("select"); - _exit(1); - } - - /* fetch a request from the main daemon */ - status = child_read_request(&state); - - if (!NT_STATUS_IS_OK(status)) { - /* we lost contact with our parent */ - _exit(0); - } - - DEBUG(4,("child daemon request %d\n", (int)state.request->cmd)); - - ZERO_STRUCTP(state.response); - state.request->null_term = '\0'; - state.mem_ctx = frame; - child_process_request(child, &state); - - DEBUG(4, ("Finished processing child request %d\n", - (int)state.request->cmd)); - - SAFE_FREE(state.request->extra_data.data); + TALLOC_FREE(frame); + } +} - iov[0].iov_base = (void *)state.response; - iov[0].iov_len = sizeof(struct winbindd_response); - iov_count = 1; +void winbind_msg_ip_dropped_parent(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_child *child; - if (state.response->length > sizeof(struct winbindd_response)) { - iov[1].iov_base = - (void *)state.response->extra_data.data; - iov[1].iov_len = state.response->length-iov[0].iov_len; - iov_count = 2; - } + winbind_msg_ip_dropped(msg_ctx, private_data, msg_type, + server_id, data); - DEBUG(10, ("Writing %d bytes to parent\n", - (int)state.response->length)); - if (write_data_iov(state.sock, iov, iov_count) != - state.response->length) { - DEBUG(0, ("Could not write result\n")); - exit(1); - } - TALLOC_FREE(frame); + for (child = winbindd_children; child != NULL; child = child->next) { + messaging_send_buf(msg_ctx, pid_to_procid(child->pid), + msg_type, data->data, data->length); } }