diff options
-rw-r--r-- | src/responder/common/responder.h | 38 | ||||
-rw-r--r-- | src/responder/common/responder_common.c | 8 | ||||
-rw-r--r-- | src/responder/common/responder_dp.c | 801 | ||||
-rw-r--r-- | src/responder/nss/nsssrv_cmd.c | 161 | ||||
-rw-r--r-- | src/responder/nss/nsssrv_private.h | 1 | ||||
-rw-r--r-- | src/responder/pam/pamsrv_cmd.c | 59 |
6 files changed, 641 insertions, 427 deletions
diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h index 1b39fdd5..3b5ab8a4 100644 --- a/src/responder/common/responder.h +++ b/src/responder/common/responder.h @@ -89,6 +89,8 @@ struct resp_ctx { struct sss_names_ctx *names; + hash_table_t *dp_request_table; + void *pvt_ctx; }; @@ -163,17 +165,41 @@ struct cli_protocol_version *register_cli_protocol_version(void); typedef void (*sss_dp_callback_t)(uint16_t err_maj, uint32_t err_min, const char *err_msg, void *ptr); -void handle_requests_after_reconnect(void); +struct dp_callback_ctx { + sss_dp_callback_t callback; + void *ptr; + + void *mem_ctx; + struct cli_ctx *cctx; +}; -int sss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *callback_memctx, - sss_dp_callback_t callback, void *callback_ctx, - int timeout, const char *domain, - bool fast_reply, int type, - const char *opt_name, uint32_t opt_id); +void handle_requests_after_reconnect(void); int responder_logrotate(DBusMessage *message, struct sbus_connection *conn); +/* Send a request to the data provider + * Once this function is called, the communication + * with the data provider will always run to + * completion. Freeing the returned tevent_req will + * cancel the notification of completion, but not + * the data provider action. + */ +struct tevent_req * +sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + int type, + const char *opt_name, + uint32_t opt_id); +errno_t +sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *err_maj, + dbus_uint32_t *err_min, + char **err_msg); + bool sss_utf8_check(const uint8_t *s, size_t n); #endif /* __SSS_RESPONDER_H__ */ diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c index f97ec06f..24f9125b 100644 --- a/src/responder/common/responder_common.c +++ b/src/responder/common/responder_common.c @@ -592,6 +592,14 @@ int sss_process_init(TALLOC_CTX *mem_ctx, return ret; } + /* Create DP request table */ + ret = sss_hash_create(rctx, 30, &rctx->dp_request_table); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Could not create hash table for the request queue\n")); + return ret; + } + DEBUG(1, ("Responder Initialization complete\n")); *responder_ctx = rctx; diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c index 2bf966cd..93c7cae4 100644 --- a/src/responder/common/responder_dp.c +++ b/src/responder/common/responder_dp.c @@ -32,14 +32,24 @@ hash_table_t *dp_requests = NULL; struct sss_dp_req; +struct dp_get_account_state { + struct resp_ctx *rctx; + struct sss_domain_info *dom; + const char *opt_name; + uint32_t opt_id; + hash_key_t *key; + + dbus_uint16_t err_maj; + dbus_uint32_t err_min; + char *err_msg; +}; + struct sss_dp_callback { struct sss_dp_callback *prev; struct sss_dp_callback *next; + struct tevent_req *req; struct sss_dp_req *sdp_req; - - sss_dp_callback_t callback; - void *callback_ctx; }; struct sss_dp_req { @@ -47,7 +57,7 @@ struct sss_dp_req { struct tevent_context *ev; DBusPendingCall *pending_reply; - char *key; + hash_key_t *key; struct tevent_timer *tev; struct sss_dp_callback *cb_list; @@ -59,7 +69,8 @@ struct sss_dp_req { static int sss_dp_callback_destructor(void *ptr) { - struct sss_dp_callback *cb = talloc_get_type(ptr, struct sss_dp_callback); + struct sss_dp_callback *cb = + talloc_get_type(ptr, struct sss_dp_callback); DLIST_REMOVE(cb->sdp_req->cb_list, cb); @@ -68,8 +79,9 @@ static int sss_dp_callback_destructor(void *ptr) static int sss_dp_req_destructor(void *ptr) { + struct sss_dp_callback *cb; struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req); - hash_key_t key; + struct dp_get_account_state *state; /* Cancel Dbus pending reply if still pending */ if (sdp_req->pending_reply) { @@ -77,15 +89,25 @@ static int sss_dp_req_destructor(void *ptr) sdp_req->pending_reply = NULL; } - /* Destroy the hash entry - * There are some situations when the entry has already - * been destroyed to avoid race condition, so don't check - * the result */ - key.type = HASH_KEY_STRING; - key.str = sdp_req->key; - int hret = hash_delete(dp_requests, &key); + /* If there are callbacks that haven't been invoked, return + * an error now. + */ + DLIST_FOR_EACH(cb, sdp_req->cb_list) { + state = tevent_req_data(cb->req, struct dp_get_account_state); + state->err_maj = DP_ERR_FATAL; + state->err_min = EIO; + tevent_req_error(cb->req, EIO); + } + + /* Destroy the hash entry */ + int hret = hash_delete(sdp_req->rctx->dp_request_table, sdp_req->key); if (hret != HASH_SUCCESS) { - DEBUG(SSSDBG_TRACE_INTERNAL, ("Could not clear entry from request queue\n")); + /* This should never happen */ + DEBUG(SSSDBG_TRACE_INTERNAL, + ("BUG: Could not clear [%d:%d:%s] from request queue: [%s]\n", + sdp_req->key->type, sdp_req->key->ul, sdp_req->key->str, + hash_error_string(hret))); + return -1; } return 0; @@ -116,180 +138,147 @@ void handle_requests_after_reconnect(void) talloc_free(sdp_req); } } - static int sss_dp_get_reply(DBusPendingCall *pending, dbus_uint16_t *err_maj, dbus_uint32_t *err_min, - char **err_msg); - -static void sss_dp_invoke_callback(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, void *ptr) + char **err_msg) { - struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req); - struct sss_dp_callback *cb; - - cb = sdp_req->cb_list; - /* Remove the callback from the list, the caller may free it, within the - * callback. */ - talloc_set_destructor((TALLOC_CTX *)cb, NULL); - DLIST_REMOVE(sdp_req->cb_list, cb); - - cb->callback(sdp_req->err_maj, - sdp_req->err_min, - sdp_req->err_msg, - cb->callback_ctx); - - /* If there are some more callbacks to be invoked, - * don't destroy the request */ - if (sdp_req->cb_list != NULL) { - return; - } - - /* steal te on rctx because it will be freed as soon as the handler - * returns. Causing a double free if we don't, as te is allocated on - * sdp_req and we are just going to free it */ - talloc_steal(sdp_req->rctx, te); - - talloc_zfree(sdp_req); -} + DBusMessage *reply; + DBusError dbus_error; + dbus_bool_t ret; + int type; + int err = EOK; -static void sdp_req_timeout(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, void *ptr) -{ - struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req); - struct sss_dp_callback *cb, *next; - struct timeval tv; - struct tevent_timer *tev; - hash_key_t key; + dbus_error_init(&dbus_error); - sdp_req->err_maj = DP_ERR_FATAL; - sdp_req->err_min = ETIMEDOUT; - sdp_req->err_msg = discard_const_p(char, "Timed out"); + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(SSSDBG_CRIT_FAILURE, + ("Severe error. A reply callback was called but no reply " + "was received and no timeout occurred\n")); - /* Destroy the hash entry */ - key.type = HASH_KEY_STRING; - key.str = sdp_req->key; - int hret = hash_delete(dp_requests, &key); - if (hret != HASH_SUCCESS) { - /* This should never happen */ - DEBUG(0, ("Could not clear entry from request queue\n")); + /* FIXME: Destroy this connection ? */ + err = EIO; + goto done; } - /* Queue up all callbacks */ - cb = sdp_req->cb_list; - tv = tevent_timeval_current(); - while (cb) { - next = cb->next; - tev = tevent_add_timer(sdp_req->ev, sdp_req, tv, - sss_dp_invoke_callback, sdp_req); - if (!tev) { - return; + type = dbus_message_get_type(reply); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + ret = dbus_message_get_args(reply, &dbus_error, + DBUS_TYPE_UINT16, err_maj, + DBUS_TYPE_UINT32, err_min, + DBUS_TYPE_STRING, err_msg, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to parse message\n")); + /* FIXME: Destroy this connection ? */ + if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); + err = EIO; + goto done; } - cb = next; - } -} - -static void sss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr) -{ - int ret; - struct sss_dp_req *sdp_req; - struct sss_dp_callback *cb, *next; - struct timeval tv; - struct tevent_timer *te; - hash_key_t key; - - sdp_req = talloc_get_type(ptr, struct sss_dp_req); + DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n", + (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg)); - /* prevent trying to cancel a reply that we already received */ - sdp_req->pending_reply = NULL; + break; - ret = sss_dp_get_reply(pending, - &sdp_req->err_maj, - &sdp_req->err_min, - &sdp_req->err_msg); - if (ret != EOK) { - if (ret == ETIME) { - sdp_req->err_maj = DP_ERR_TIMEOUT; - sdp_req->err_min = ret; - sdp_req->err_msg = talloc_strdup(sdp_req, "Request timed out"); - } - else { - sdp_req->err_maj = DP_ERR_FATAL; - sdp_req->err_min = ret; - sdp_req->err_msg = - talloc_strdup(sdp_req, - "Failed to get reply from Data Provider"); + case DBUS_MESSAGE_TYPE_ERROR: + if (strcmp(dbus_message_get_error_name(reply), + DBUS_ERROR_NO_REPLY) == 0) { + err = ETIME; + goto done; } - } + DEBUG(0,("The Data Provider returned an error [%s]\n", + dbus_message_get_error_name(reply))); + /* Falling through to default intentionally*/ + default: + /* + * Timeout or other error occurred or something + * unexpected happened. + * It doesn't matter which, because either way we + * know that this connection isn't trustworthy. + * We'll destroy it now. + */ - /* Check whether we need to issue any callbacks */ - if (sdp_req->cb_list == NULL) { - /* No callbacks to invoke. Destroy the hash entry */ - talloc_zfree(sdp_req); - return; + /* FIXME: Destroy this connection ? */ + err = EIO; } - /* Destroy the hash entry */ - key.type = HASH_KEY_STRING; - key.str = sdp_req->key; - int hret = hash_delete(dp_requests, &key); - if (hret != HASH_SUCCESS) { - /* This should never happen */ - DEBUG(0, ("Could not clear entry from request queue\n")); - } +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); - /* Queue up all callbacks */ - cb = sdp_req->cb_list; - tv = tevent_timeval_current(); - while (cb) { - next = cb->next; - te = tevent_add_timer(sdp_req->ev, sdp_req, tv, - sss_dp_invoke_callback, sdp_req); - if (!te) { - /* Out of memory or other serious error */ - return; - } - cb = next; - } + return err; } -static int sss_dp_send_acct_req_create(struct resp_ctx *rctx, - TALLOC_CTX *callback_memctx, - const char *domain, - uint32_t be_type, - char *filter, - int timeout, - sss_dp_callback_t callback, - void *callback_ctx, - struct sss_dp_req **ndp); - -int sss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *callback_memctx, - sss_dp_callback_t callback, void *callback_ctx, - int timeout, const char *domain, - bool fast_reply, int type, - const char *opt_name, uint32_t opt_id) +static struct tevent_req * +sss_dp_get_account_int_send(struct resp_ctx *rctx, + hash_key_t *key, + struct sss_domain_info *dom, + uint32_t be_type, + const char *filter); + +static void +sss_dp_get_account_done(struct tevent_req *subreq); + +static errno_t +sss_dp_get_account_int_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *err_maj, + dbus_uint32_t *err_min, + char **err_msg); + + +/* Send a request to the data provider + * Once this function is called, the communication + * with the data provider will always run to + * completion. Freeing the returned tevent_req will + * cancel the notification of completion, but not + * the data provider action. + */ +struct tevent_req * +sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + int type, + const char *opt_name, + uint32_t opt_id) { - int ret, hret; - uint32_t be_type; + errno_t ret; + int hret; + struct tevent_req *req; + struct tevent_req *subreq; + struct dp_get_account_state *state; + struct sss_dp_req *sdp_req; + struct sss_dp_callback *cb; char *filter; - hash_key_t key; + uint32_t be_type; hash_value_t value; - TALLOC_CTX *tmp_ctx; - struct timeval tv; - struct sss_dp_req *sdp_req = NULL; - struct sss_dp_callback *cb; + + req = tevent_req_create(mem_ctx, &state, struct dp_get_account_state); + if (!req) { + return NULL; + } /* either, or, not both */ if (opt_name && opt_id) { - return EINVAL; + ret = EINVAL; + goto error; } - if (!domain) { - return EINVAL; + if (!dom) { + ret = EINVAL; + goto error; } + state->rctx = rctx; + state->dom = dom; + switch (type) { case SSS_DP_USER: be_type = BE_REQ_USER; @@ -304,160 +293,233 @@ int sss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *callback_memctx, be_type = BE_REQ_NETGROUP; break; default: - return EINVAL; + ret = EINVAL; + goto error; } if (fast_reply) { be_type |= BE_REQ_FAST; } - if (dp_requests == NULL) { - /* Create a hash table to handle queued update requests */ - ret = hash_create(10, &dp_requests, NULL, NULL); - if (ret != HASH_SUCCESS) { - fprintf(stderr, "cannot create hash table (%s)\n", hash_error_string(ret)); - return EIO; - } - } + /* Check whether there's already an identical request in progress */ - tmp_ctx = talloc_new(NULL); - if (!tmp_ctx) { - return ENOMEM; + state->key = talloc(state, hash_key_t); + if (!state->key) { + ret = ENOMEM; + goto error; } - key.type = HASH_KEY_STRING; - key.str = NULL; + state->key->type = HASH_KEY_STRING; if (opt_name) { - filter = talloc_asprintf(tmp_ctx, "name=%s", opt_name); - key.str = talloc_asprintf(tmp_ctx, "%d%s@%s", type, opt_name, domain); + filter = talloc_asprintf(state, "name=%s", opt_name); + state->key->str = talloc_asprintf(state->key, "%d:%s@%s", + type, opt_name, dom->name); } else if (opt_id) { - filter = talloc_asprintf(tmp_ctx, "idnumber=%u", opt_id); - key.str = talloc_asprintf(tmp_ctx, "%d%d@%s", type, opt_id, domain); + filter = talloc_asprintf(state, "idnumber=%u", opt_id); + state->key->str = talloc_asprintf(state->key, "%d:%d@%s", + type, opt_id, dom->name); } else { - filter = talloc_strdup(tmp_ctx, ENUM_INDICATOR); - key.str = talloc_asprintf(tmp_ctx, "%d*@%s", type, domain); + filter = talloc_strdup(state, ENUM_INDICATOR); + state->key->str = talloc_asprintf(state->key, "%d:*@%s", + type, dom->name); } - if (!filter || !key.str) { - talloc_zfree(tmp_ctx); - return ENOMEM; + if (!filter || !state->key->str) { + ret = ENOMEM; } - /* Check whether there's already a request in progress */ - hret = hash_lookup(dp_requests, &key, &value); + /* Check the hash for existing references to this request */ + hret = hash_lookup(rctx->dp_request_table, state->key, &value); switch (hret) { case HASH_SUCCESS: - /* Request already in progress - * Add an additional callback if needed and return - */ - DEBUG(2, ("Identical request in progress\n")); - - if (callback) { - /* We have a new request asking for a callback */ - sdp_req = talloc_get_type(value.ptr, struct sss_dp_req); - if (!sdp_req) { - DEBUG(0, ("Could not retrieve DP request context\n")); - ret = EIO; - goto done; - } - - cb = talloc_zero(callback_memctx, struct sss_dp_callback); - if (!cb) { - ret = ENOMEM; - goto done; - } - - cb->callback = callback; - cb->callback_ctx = callback_ctx; - cb->sdp_req = sdp_req; - - DLIST_ADD_END(sdp_req->cb_list, cb, struct sss_dp_callback *); - talloc_set_destructor((TALLOC_CTX *)cb, sss_dp_callback_destructor); - } - - ret = EOK; + /* Request already in progress */ + DEBUG(SSSDBG_TRACE_FUNC, + ("Identical request in progress: [%s]\n", state->key->str)); break; case HASH_ERROR_KEY_NOT_FOUND: /* No such request in progress * Create a new request */ - ret = sss_dp_send_acct_req_create(rctx, callback_memctx, domain, - be_type, filter, timeout, - callback, callback_ctx, - &sdp_req); - if (ret != EOK) { - goto done; - } value.type = HASH_VALUE_PTR; - value.ptr = sdp_req; - hret = hash_enter(dp_requests, &key, &value); - if (hret != HASH_SUCCESS) { - DEBUG(0, ("Could not store request query (%s)\n", - hash_error_string(hret))); - talloc_zfree(sdp_req); - ret = EIO; - goto done; - } - - sdp_req->key = talloc_strdup(sdp_req, key.str); - - tv = tevent_timeval_current_ofs(timeout, 0); - sdp_req->tev = tevent_add_timer(sdp_req->ev, sdp_req, tv, - sdp_req_timeout, sdp_req); - if (!sdp_req->tev) { - DEBUG(0, ("Out of Memory!?\n")); - talloc_zfree(sdp_req); + subreq = sss_dp_get_account_int_send(rctx, state->key, dom, + be_type, filter); + if (!subreq) { ret = ENOMEM; - goto done; + goto error; } + tevent_req_set_callback(subreq, sss_dp_get_account_done, NULL); - talloc_set_destructor((TALLOC_CTX *)sdp_req, sss_dp_req_destructor); + /* We should now be able to find the sdp_req in the hash table */ + hret = hash_lookup(rctx->dp_request_table, state->key, &value); + if (hret != HASH_SUCCESS) { + /* Something must have gone wrong with creating the request */ + talloc_zfree(subreq); + ret = EIO; + goto error; + } - ret = EOK; break; default: - DEBUG(0,("Could not query request list (%s)\n", - hash_error_string(hret))); - talloc_zfree(sdp_req); + DEBUG(SSSDBG_CRIT_FAILURE, + ("Could not query request list (%s)\n", + hash_error_string(hret))); ret = EIO; + goto error; } -done: - talloc_zfree(tmp_ctx); - return ret; + /* Register this request for results */ + sdp_req = talloc_get_type(value.ptr, struct sss_dp_req); + if (!sdp_req) { + DEBUG(0, ("Could not retrieve DP request context\n")); + ret = EIO; + goto error; + } + + cb = talloc_zero(state, struct sss_dp_callback); + if (!cb) { + ret = ENOMEM; + goto error; + } + + cb->req = req; + cb->sdp_req = sdp_req; + + /* Add it to the list of requests to call */ + DLIST_ADD_END(sdp_req->cb_list, cb, + struct sss_dp_callback *); + talloc_set_destructor((TALLOC_CTX *)cb, + sss_dp_callback_destructor); + + return req; + +error: + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + return req; } -static int sss_dp_send_acct_req_create(struct resp_ctx *rctx, - TALLOC_CTX *callback_memctx, - const char *domain, - uint32_t be_type, - char *filter, - int timeout, - sss_dp_callback_t callback, - void *callback_ctx, - struct sss_dp_req **ndp) +static void +sss_dp_get_account_done(struct tevent_req *subreq) { - DBusMessage *msg; + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct dp_get_account_state *state = + tevent_req_data(req, struct dp_get_account_state); + + ret = sss_dp_get_account_int_recv(state, req, + &state->err_maj, + &state->err_min, + &state->err_msg); + if (ret != EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } +} + +errno_t +sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *err_maj, + dbus_uint32_t *err_min, + char **err_msg) +{ + struct dp_get_account_state *state = + tevent_req_data(req, struct dp_get_account_state); + + enum tevent_req_state TRROEstate; + uint64_t TRROEerr; + + *err_maj = state->err_maj; + *err_min = state->err_min; + *err_msg = talloc_steal(mem_ctx, state->err_msg); + + if (tevent_req_is_error(req, &TRROEstate, &TRROEerr)) { + if (TRROEstate == TEVENT_REQ_USER_ERROR) { + *err_maj = DP_ERR_FATAL; + *err_min = TRROEerr; + } else { + return EIO; + } + } + + return EOK; +} + +struct dp_get_account_int_state { + struct resp_ctx *rctx; + struct sss_domain_info *dom; + uint32_t be_type; + const char *filter; + + struct sss_dp_req *sdp_req; DBusPendingCall *pending_reply; +}; + +static void sss_dp_get_account_int_done(DBusPendingCall *pending, void *ptr); + +static struct tevent_req * +sss_dp_get_account_int_send(struct resp_ctx *rctx, + hash_key_t *key, + struct sss_domain_info *dom, + uint32_t be_type, + const char *filter) +{ + errno_t ret; + int hret; + struct tevent_req *req; + struct dp_get_account_int_state *state; + struct be_conn *be_conn; + DBusMessage *msg; dbus_bool_t dbret; - struct sss_dp_callback *cb; - struct sss_dp_req *sdp_req; + bool msg_created = false; + hash_value_t value; uint32_t attrs = BE_ATTR_CORE; - struct be_conn *be_conn; - int ret; + + /* Internal requests need to be allocated on the responder context + * so that they don't go away if a client disconnects. The worst- + * case scenario here is that the cache is updated without any + * client expecting a response. + */ + req = tevent_req_create(rctx, + &state, + struct dp_get_account_int_state); + if (!req) return NULL; + + state->rctx = rctx; + state->dom = dom; + state->be_type = be_type; + state->filter = filter; + + state->sdp_req = talloc_zero(state, struct sss_dp_req); + if (!state->sdp_req) { + ret = ENOMEM; + goto error; + } + state->sdp_req->rctx = rctx; + state->sdp_req->ev = rctx->ev; + + /* Copy the key to use when calling the destructor + * It needs to be a copy because the original request + * might be freed if it no longer cares about the reply. + */ + state->sdp_req->key = talloc_steal(state->sdp_req, key); /* double check dp_ctx has actually been initialized. * in some pathological cases it may happen that nss starts up before * dp connection code is actually able to establish a connection. */ - ret = sss_dp_get_domain_conn(rctx, domain, &be_conn); + ret = sss_dp_get_domain_conn(rctx, dom->name, &be_conn); if (ret != EOK) { - DEBUG(1, ("The Data Provider connection for %s is not available!" - " This maybe a bug, it shouldn't happen!\n", domain)); - return EIO; + DEBUG(SSSDBG_CRIT_FAILURE, + ("BUG: The Data Provider connection for %s is not available!", + dom->name)); + ret = EIO; + goto error; } /* create the message */ @@ -467,11 +529,14 @@ static int sss_dp_send_acct_req_create(struct resp_ctx *rctx, DP_METHOD_GETACCTINFO); if (msg == NULL) { DEBUG(0,("Out of memory?!\n")); - return ENOMEM; + ret = ENOMEM; + goto error; } + msg_created = true; - DEBUG(4, ("Sending request for [%s][%u][%d][%s]\n", - domain, be_type, attrs, filter)); + DEBUG(SSSDBG_TRACE_FUNC, + ("Sending request for [%s][%u][%d][%s]\n", + dom->name, be_type, attrs, filter)); dbret = dbus_message_append_args(msg, DBUS_TYPE_UINT32, &be_type, @@ -480,125 +545,141 @@ static int sss_dp_send_acct_req_create(struct resp_ctx *rctx, DBUS_TYPE_INVALID); if (!dbret) { DEBUG(1,("Failed to build message\n")); - return EIO; - } - - sdp_req = talloc_zero(rctx, struct sss_dp_req); - if (!sdp_req) { - dbus_message_unref(msg); - return ENOMEM; + ret = EIO; + goto error; } - sdp_req->rctx = rctx; - ret = sbus_conn_send(be_conn->conn, msg, timeout, - sss_dp_send_acct_callback, - sdp_req, &pending_reply); + ret = sbus_conn_send(be_conn->conn, msg, + SSS_CLI_SOCKET_TIMEOUT / 2, + sss_dp_get_account_int_done, + req, + &state->sdp_req->pending_reply); dbus_message_unref(msg); + msg_created = false; if (ret != EOK) { /* * Critical Failure * We can't communicate on this connection - * We'll drop it using the default destructor. */ - DEBUG(0, ("D-BUS send failed.\n")); - return EIO; + DEBUG(SSSDBG_CRIT_FAILURE, + ("D-BUS send failed.\n")); + ret = EIO; + goto error; } - sdp_req->ev = rctx->ev; - sdp_req->pending_reply = pending_reply; + /* Add this sdp_req to the hash table */ + value.type = HASH_VALUE_PTR; + value.ptr = state->sdp_req; - if (callback) { - cb = talloc_zero(callback_memctx, struct sss_dp_callback); - if (!cb) { - talloc_zfree(sdp_req); - return ENOMEM; - } - cb->callback = callback; - cb->callback_ctx = callback_ctx; - cb->sdp_req = sdp_req; - - DLIST_ADD(sdp_req->cb_list, cb); - talloc_set_destructor((TALLOC_CTX *)cb, sss_dp_callback_destructor); + hret = hash_enter(rctx->dp_request_table, key, &value); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Could not store request query (%s)\n", + hash_error_string(hret))); + ret = EIO; + goto error; } + talloc_set_destructor((TALLOC_CTX *)state->sdp_req, + sss_dp_req_destructor); - *ndp = sdp_req; + return req; - return EOK; +error: + if (msg_created) { + dbus_message_unref(msg); + } + + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + return req; } -static int sss_dp_get_reply(DBusPendingCall *pending, - dbus_uint16_t *err_maj, - dbus_uint32_t *err_min, - char **err_msg) +static void sss_dp_get_account_int_done(DBusPendingCall *pending, void *ptr) { - DBusMessage *reply; - DBusError dbus_error; - dbus_bool_t ret; - int type; - int err = EOK; + int ret; + struct tevent_req *req; + struct sss_dp_req *sdp_req; + struct sss_dp_callback *cb; + struct dp_get_account_int_state *state; + struct dp_get_account_state *cb_state; - dbus_error_init(&dbus_error); + req = talloc_get_type(ptr, struct tevent_req); + state = tevent_req_data(req, struct dp_get_account_int_state); + sdp_req = state->sdp_req; - reply = dbus_pending_call_steal_reply(pending); - if (!reply) { - /* reply should never be null. This function shouldn't be called - * until reply is valid or timeout has occurred. If reply is NULL - * here, something is seriously wrong and we should bail out. - */ - DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); + /* prevent trying to cancel a reply that we already received */ + sdp_req->pending_reply = NULL; - /* FIXME: Destroy this connection ? */ - err = EIO; - goto done; + ret = sss_dp_get_reply(pending, + &sdp_req->err_maj, + &sdp_req->err_min, + &sdp_req->err_msg); + if (ret != EOK) { + if (ret == ETIME) { + sdp_req->err_maj = DP_ERR_TIMEOUT; + sdp_req->err_min = ret; + sdp_req->err_msg = talloc_strdup(sdp_req, "Request timed out"); + } + else { + sdp_req->err_maj = DP_ERR_FATAL; + sdp_req->err_min = ret; + sdp_req->err_msg = + talloc_strdup(sdp_req, + "Failed to get reply from Data Provider"); + } } - type = dbus_message_get_type(reply); - switch (type) { - case DBUS_MESSAGE_TYPE_METHOD_RETURN: - ret = dbus_message_get_args(reply, &dbus_error, - DBUS_TYPE_UINT16, err_maj, - DBUS_TYPE_UINT32, err_min, - DBUS_TYPE_STRING, err_msg, - DBUS_TYPE_INVALID); - if (!ret) { - DEBUG(1,("Failed to parse message\n")); - /* FIXME: Destroy this connection ? */ - if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); - err = EIO; - goto done; + /* Check whether we need to issue any callbacks */ + DLIST_FOR_EACH(cb, sdp_req->cb_list) { + cb_state = tevent_req_data(cb->req, struct dp_get_account_state); + cb_state->err_maj = sdp_req->err_maj; + cb_state->err_min = sdp_req->err_min; + cb_state->err_msg = talloc_strdup(cb_state, sdp_req->err_msg); + /* Don't bother checking for NULL. If it fails due to ENOMEM, + * we can't really handle it annyway. + */ + + if (ret == EOK) { + tevent_req_done(cb->req); + } else { + tevent_req_error(cb->req, ret); } - DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n", - (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg)); + /* Remove each callback from the list as it's replied to */ + DLIST_REMOVE(sdp_req->cb_list, cb); + } - break; + /* We're done with this request. Free the sdp_req + * This will clean up the hash table entry as well + */ + talloc_zfree(sdp_req); +} - case DBUS_MESSAGE_TYPE_ERROR: - if (strcmp(dbus_message_get_error_name(reply), - DBUS_ERROR_NO_REPLY) == 0) { - err = ETIME; - goto done; - } - DEBUG(0,("The Data Provider returned an error [%s]\n", - dbus_message_get_error_name(reply))); - /* Falling through to default intentionally*/ - default: - /* - * Timeout or other error occurred or something - * unexpected happened. - * It doesn't matter which, because either way we - * know that this connection isn't trustworthy. - * We'll destroy it now. - */ +static errno_t +sss_dp_get_account_int_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *err_maj, + dbus_uint32_t *err_min, + char **err_msg) +{ + struct dp_get_account_int_state *state = + tevent_req_data(req, struct dp_get_account_int_state); - /* FIXME: Destroy this connection ? */ - err = EIO; - } + enum tevent_req_state TRROEstate; + uint64_t TRROEerr; -done: - dbus_pending_call_unref(pending); - dbus_message_unref(reply); + *err_maj = state->sdp_req->err_maj; + *err_min = state->sdp_req->err_min; + *err_msg = talloc_steal(mem_ctx, state->sdp_req->err_msg); - return err; -} + if (tevent_req_is_error(req, &TRROEstate, &TRROEerr)) { + if (TRROEstate == TEVENT_REQ_USER_ERROR) { + *err_maj = DP_ERR_FATAL; + *err_min = TRROEerr; + } else { + return EIO; + } + } + return EOK; +} diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index a37bd766..fcfacb73 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -533,6 +533,7 @@ static int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter) return EOK; } +static void nsssrv_dp_send_acct_req_done(struct tevent_req *req); /* FIXME: do not check res->count, but get in a msgs and check in parent */ /* FIXME: do not sss_cmd_done, but return error and let parent do it */ @@ -546,7 +547,6 @@ errno_t check_cache(struct nss_dom_ctx *dctx, void *pvt) { errno_t ret; - int timeout; time_t now; uint64_t lastUpdate; uint64_t cacheExpire = 0; @@ -554,6 +554,8 @@ errno_t check_cache(struct nss_dom_ctx *dctx, struct nss_cmd_ctx *cmdctx = dctx->cmdctx; struct cli_ctx *cctx = cmdctx->cctx; bool off_band_update = false; + struct tevent_req *req = NULL; + struct dp_callback_ctx *cb_ctx = NULL; /* when searching for a user or netgroup, more than one reply is a * db error @@ -620,26 +622,27 @@ errno_t check_cache(struct nss_dom_ctx *dctx, } if (off_band_update) { - - timeout = SSS_CLI_SOCKET_TIMEOUT/2; - /* No callback required * This was an out-of-band update. We'll return EOK * so the calling function can return the cached entry * immediately. */ - ret = sss_dp_send_acct_req(cctx->rctx, NULL, NULL, NULL, - timeout, dctx->domain->name, - true, req_type, - opt_name, opt_id); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); + req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true, + req_type, opt_name, opt_id); + if (!req) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Out of memory sending out-of-band data provider " + "request\n")); + /* This is non-fatal, so we'll continue here */ } else { - - DEBUG(3, ("Updating cache out-of-band\n")); + DEBUG(SSSDBG_TRACE_FUNC, ("Updating cache out-of-band\n")); } + /* We don't need to listen for a reply, so we will free the + * request here. + */ + talloc_zfree(req); + } else { /* This is a cache miss. Or the cache is expired. * We need to get the updated user information before returning it. @@ -647,32 +650,66 @@ errno_t check_cache(struct nss_dom_ctx *dctx, /* dont loop forever :-) */ dctx->check_provider = false; - timeout = SSS_CLI_SOCKET_TIMEOUT/2; /* keep around current data in case backend is offline */ if (res->count) { dctx->res = talloc_steal(dctx, res); } - ret = sss_dp_send_acct_req(cctx->rctx, cmdctx, - callback, pvt, timeout, - dctx->domain->name, - true, req_type, - opt_name, opt_id); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR_CODE(cctx, EIO); - } - sss_cmd_done(cctx, cmdctx); + req = sss_dp_get_account_send(dctx, cctx->rctx, dctx->domain, true, + req_type, opt_name, opt_id); + if (!req) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Out of memory sending data provider request\n")); + ret = ENOMEM; + goto error; } + cb_ctx = talloc_zero(dctx, struct dp_callback_ctx); + if(!cb_ctx) { + talloc_zfree(req); + ret = ENOMEM; + goto error; + } + cb_ctx->callback = callback; + cb_ctx->ptr = dctx; + cb_ctx->cctx = dctx->cmdctx->cctx; + cb_ctx->mem_ctx = dctx; + + tevent_req_set_callback(req, nsssrv_dp_send_acct_req_done, cb_ctx); + return EAGAIN; } return EOK; + +error: + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR_CODE(cctx, ret); + } + sss_cmd_done(cctx, cmdctx); + return EOK; +} + +static void nsssrv_dp_send_acct_req_done(struct tevent_req *req) +{ + struct dp_callback_ctx *cb_ctx = + tevent_req_callback_data(req, struct dp_callback_ctx); + + errno_t ret; + dbus_uint16_t err_maj; + dbus_uint32_t err_min; + char *err_msg; + + ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req, + &err_maj, &err_min, + &err_msg); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cb_ctx->cctx); + } + + cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr); } static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min, @@ -1274,6 +1311,7 @@ struct tevent_req *nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx, step_ctx->nctx = state->nctx; step_ctx->getent_ctx = state->getent_ctx; step_ctx->rctx = client->rctx; + step_ctx->cctx = client; step_ctx->returned_to_mainloop = false; ret = nss_cmd_setpwent_step(step_ctx); @@ -1315,7 +1353,8 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx) struct setent_req_list *req; struct timeval tv; struct tevent_timer *te; - int timeout; + struct tevent_req *dpreq; + struct dp_callback_ctx *cb_ctx; while (dom) { while (dom && dom->enumerate == 0) { @@ -1347,18 +1386,29 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx) step_ctx->returned_to_mainloop = true; /* Only do this once per provider */ dctx->check_provider = false; - timeout = SSS_CLI_SOCKET_TIMEOUT; - ret = sss_dp_send_acct_req(rctx, step_ctx, - nss_cmd_setpwent_dp_callback, step_ctx, - timeout, dom->name, true, - SSS_DP_USER, NULL, 0); - if (ret == EOK) { - return EAGAIN; + dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true, + SSS_DP_USER, NULL, 0); + if (!dpreq) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Enum Cache refresh for domain [%s] failed." + " Trying to return what we have in cache!\n", + dom->name)); } else { - DEBUG(2, ("Enum Cache refresh for domain [%s] failed." - " Trying to return what we have in cache!\n", - dom->name)); + cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx); + if(!cb_ctx) { + talloc_zfree(dpreq); + return ENOMEM; + } + + cb_ctx->callback = nss_cmd_setpwent_dp_callback; + cb_ctx->ptr = step_ctx; + cb_ctx->cctx = step_ctx->cctx; + cb_ctx->mem_ctx = step_ctx; + + tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx); + + return EAGAIN; } } @@ -2541,6 +2591,7 @@ struct tevent_req *nss_cmd_setgrent_send(TALLOC_CTX *mem_ctx, step_ctx->nctx = state->nctx; step_ctx->getent_ctx = state->getent_ctx; step_ctx->rctx = client->rctx; + step_ctx->cctx = client; step_ctx->returned_to_mainloop = false; ret = nss_cmd_setgrent_step(step_ctx); @@ -2582,7 +2633,8 @@ static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx) struct setent_req_list *req; struct timeval tv; struct tevent_timer *te; - int timeout; + struct tevent_req *dpreq; + struct dp_callback_ctx *cb_ctx; while (dom) { while (dom && dom->enumerate == 0) { @@ -2613,19 +2665,28 @@ static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx) if (dctx->check_provider) { step_ctx->returned_to_mainloop = true; /* Only do this once per provider */ - dctx->check_provider = false; - timeout = SSS_CLI_SOCKET_TIMEOUT; + dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true, + SSS_DP_USER, NULL, 0); + if (!dpreq) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Enum Cache refresh for domain [%s] failed." + " Trying to return what we have in cache!\n", + dom->name)); + } else { + cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx); + if(!cb_ctx) { + talloc_zfree(dpreq); + return ENOMEM; + } + + cb_ctx->callback = nss_cmd_setgrent_dp_callback; + cb_ctx->ptr = step_ctx; + cb_ctx->cctx = step_ctx->cctx; + cb_ctx->mem_ctx = step_ctx; + + tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx); - ret = sss_dp_send_acct_req(rctx, step_ctx, - nss_cmd_setgrent_dp_callback, step_ctx, - timeout, dom->name, true, - SSS_DP_GROUP, NULL, 0); - if (ret == EOK) { return EAGAIN; - } else { - DEBUG(2, ("Enum Cache refresh for domain [%s] failed." - " Trying to return what we have in cache!\n", - dom->name)); } } diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h index 27b0e567..2aa322c5 100644 --- a/src/responder/nss/nsssrv_private.h +++ b/src/responder/nss/nsssrv_private.h @@ -85,6 +85,7 @@ struct setent_step_ctx { struct nss_dom_ctx *dctx; struct getent_ctx *getent_ctx; struct resp_ctx *rctx; + struct cli_ctx *cctx; bool check_next; bool returned_to_mainloop; diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index 18ba3fdf..2a000f09 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -807,6 +807,8 @@ done: return pam_check_user_done(preq, ret); } +static void pam_dp_send_acct_req_done(struct tevent_req *req); + static int pam_check_user_search(struct pam_auth_req *preq) { struct sss_domain_info *dom = preq->domain; @@ -815,6 +817,8 @@ static int pam_check_user_search(struct pam_auth_req *preq) struct sysdb_ctx *sysdb; time_t cacheExpire; int ret; + struct tevent_req *dpreq; + struct dp_callback_ctx *cb_ctx; while (dom) { /* if it is a domainless search, skip domains that require fully @@ -904,18 +908,28 @@ static int pam_check_user_search(struct pam_auth_req *preq) /* dont loop forever :-) */ preq->check_provider = false; - ret = sss_dp_send_acct_req(preq->cctx->rctx, preq, - pam_check_user_dp_callback, preq, - SSS_CLI_SOCKET_TIMEOUT/2, - dom->name, false, - SSS_DP_INITGROUPS, - name, 0); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); - preq->pd->pam_status = PAM_SYSTEM_ERR; - return EIO; + dpreq = sss_dp_get_account_send(preq, preq->cctx->rctx, + dom, false, SSS_DP_INITGROUPS, + name, 0); + if (!dpreq) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Out of memory sending data provider request\n")); + return ENOMEM; + } + + cb_ctx = talloc_zero(preq, struct dp_callback_ctx); + if(!cb_ctx) { + talloc_zfree(dpreq); + return ENOMEM; } + + cb_ctx->callback = pam_check_user_dp_callback; + cb_ctx->ptr = preq; + cb_ctx->cctx = preq->cctx; + cb_ctx->mem_ctx = preq; + + tevent_req_set_callback(dpreq, pam_dp_send_acct_req_done, cb_ctx); + /* tell caller we are in an async call */ return EAGAIN; } @@ -924,6 +938,29 @@ static int pam_check_user_search(struct pam_auth_req *preq) return ENOENT; } +static void pam_dp_send_acct_req_done(struct tevent_req *req) +{ + struct dp_callback_ctx *cb_ctx = + tevent_req_callback_data(req, struct dp_callback_ctx); + + errno_t ret; + dbus_uint16_t err_maj; + dbus_uint32_t err_min; + char *err_msg; + + ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req, + &err_maj, &err_min, + &err_msg); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Fatal error, killing connection!\n")); + talloc_free(cb_ctx->cctx); + return; + } + + cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr); +} + static int pam_check_user_done(struct pam_auth_req *preq, int ret) { switch (ret) { |