From fa665b2e0d06e8c2dd59641d9d40c15ad0d8110d Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Wed, 5 Dec 2012 17:40:45 +0000 Subject: Add backchannel NSS provider query on initgr calls This is needed in order to assure the memcache is properly and promptly cleaned up if a user memberships change on login. The list of the current groups for the user is sourced before it is updated and sent to the NSS provider to verify if it has changed after the update call has been made. --- src/providers/data_provider_be.c | 165 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c index 76912a6fe..4749fc28c 100644 --- a/src/providers/data_provider_be.c +++ b/src/providers/data_provider_be.c @@ -560,6 +560,160 @@ static void acctinfo_callback(struct be_req *req, talloc_free(req); } +struct be_initgr_prereq { + char *user; + char *domain; + uint32_t gnum; + uint32_t *groups; + + void *orig_pvt_data; + int orig_dp_err_type; + int orig_errnum; + const char *orig_errstr; +}; + +static void acctinfo_callback_initgr_wrap(struct be_req *be_req) +{ + struct be_initgr_prereq *pr = talloc_get_type(be_req->pvt, + struct be_initgr_prereq); + + be_req->pvt = pr->orig_pvt_data; + acctinfo_callback(be_req, pr->orig_dp_err_type, + pr->orig_errnum, pr->orig_errstr); +} + +static void acctinfo_callback_initgr_sbus(DBusPendingCall *pending, void *ptr) +{ + struct be_req *be_req = talloc_get_type(ptr, struct be_req); + + dbus_pending_call_unref(pending); + + acctinfo_callback_initgr_wrap(be_req); +} + +static void acctinfo_initgroups_callback(struct be_req *be_req, + int dp_err_type, + int errnum, + const char *errstr) +{ + struct be_initgr_prereq *pr = talloc_get_type(be_req->pvt, + struct be_initgr_prereq); + DBusMessage *msg = NULL; + dbus_bool_t dbret; + int ret; + + pr->orig_dp_err_type = dp_err_type; + pr->orig_errnum = errnum; + pr->orig_errstr = errstr; + + if (!be_req->be_ctx->nss_cli || !be_req->be_ctx->nss_cli->conn) { + DEBUG(SSSDBG_MINOR_FAILURE, ("NSS Service not conected\n")); + ret = EACCES; + goto done; + } + + /* Set up null request */ + msg = dbus_message_new_method_call(NULL, + DP_PATH, + DP_INTERFACE, + DP_REV_METHOD_INITGR_CHECK); + if (!msg) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory?!\n")); + ret = ENOMEM; + goto done; + } + + dbret = dbus_message_append_args(msg, + DBUS_TYPE_STRING, &pr->user, + DBUS_TYPE_STRING, &pr->domain, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + &pr->groups, pr->gnum, + DBUS_TYPE_INVALID); + if (!dbret) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory?!\n")); + ret = ENOMEM; + goto done; + } + + /* ping the NSS service, no reply expected */ + ret = sbus_conn_send(be_req->be_ctx->nss_cli->conn, msg, -1, + acctinfo_callback_initgr_sbus, be_req, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_FUNC, + ("Error contacting NSS responder: %d [%s]\n", + ret, strerror(ret))); + } + +done: + if (msg) { + dbus_message_unref(msg); + } + if (ret != EOK) { + /* return immediately if we cannot contact nss provider */ + acctinfo_callback_initgr_wrap(be_req); + } +} + +static errno_t be_initgroups_prereq(struct be_req *be_req) +{ + struct be_acct_req *ar = talloc_get_type(be_req->req_data, + struct be_acct_req); + struct be_initgr_prereq *pr; + struct ldb_result *res; + errno_t ret; + const char *tmpstr; + int i; + + ret = sysdb_initgroups(be_req, be_req->be_ctx->sysdb, + ar->filter_value, &res); + if (ret && ret != ENOENT) { + return ret; + } + /* if the user is completely missing or has no group memberships + * at all there is no need to contact NSS, it would be a noop */ + if (ret == ENOENT || res->count == 0 || res->count == 1) { + /* yet unknown, ignore */ + return EOK; + } + + pr = talloc(be_req, struct be_initgr_prereq); + if (!pr) { + return ENOMEM; + } + pr->groups = talloc_array(pr, gid_t, res->count - 1); + if (!pr->groups) { + return ENOMEM; + } + tmpstr = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL); + if (!tmpstr) { + return EINVAL; + } + pr->user = talloc_strdup(pr, tmpstr); + if (!pr->user) { + return ENOMEM; + } + pr->domain = talloc_strdup(pr, be_req->be_ctx->domain->name); + if (!pr->domain) { + return ENOMEM; + } + for (pr->gnum = 0, i = 1; i < res->count; i++) { + pr->groups[pr->gnum] = ldb_msg_find_attr_as_uint(res->msgs[i], + SYSDB_GIDNUM, 0); + /* if 0 it may be a non-posix group, so we skip it */ + if (pr->groups[pr->gnum] != 0) { + pr->gnum++; + } + } + + talloc_zfree(res); + + pr->orig_pvt_data = be_req->pvt; + be_req->pvt = pr; + be_req->fn = acctinfo_initgroups_callback; + + return EOK; +} + static errno_t split_name_extended(TALLOC_CTX *mem_ctx, const char *filter, @@ -737,6 +891,17 @@ static int be_get_account_info(DBusMessage *message, struct sbus_connection *con goto done; } + /* see if we need a pre request call, only done for initgroups for now */ + if ((type & 0xFF) == BE_REQ_INITGROUPS) { + ret = be_initgroups_prereq(be_req); + if (ret) { + err_maj = DP_ERR_FATAL; + err_min = ret; + err_msg = "Prerequest failed"; + goto done; + } + } + /* process request */ ret = be_file_request(becli->bectx->bet_info[BET_ID].pvt_bet_data, -- cgit