data_provider_be.c revision 19b4bb652f5cdc2797b66595eaf8811881aa9873
/*
SSSD
Data Provider Process
Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include <popt.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include "sbus/sssd_dbus.h"
#include "providers/dp_backend.h"
#include "providers/fail_over.h"
#include "providers/dp_refresh.h"
#include "providers/dp_ptask.h"
#include "util/child_common.h"
#include "resolv/async_resolv.h"
#include "monitor/monitor_interfaces.h"
#define MSG_TARGET_NO_CONFIGURED "sssd_be: The requested target is not configured"
#define ACCESS_PERMIT "permit"
#define ACCESS_DENY "deny"
#define NO_PROVIDER "none"
struct sbus_connection *conn);
struct sbus_connection *conn);
struct sbus_connection *conn);
struct sbus_connection *conn);
struct sbus_method monitor_be_methods[] = {
};
struct sbus_interface monitor_be_interface = {
};
struct sbus_method be_methods[] = {
};
struct sbus_interface be_interface = {
};
};
#define REQ_PHASE_ACCESS 0
#define REQ_PHASE_SELINUX 1
struct be_req {
void *req_data;
void *pvt;
/* This is utilized in access provider
* request handling to indicate if access or
* selinux provider is calling the callback.
*/
int phase;
};
{
return be_req;
}
{
}
{
}
{
}
struct be_async_req {
};
struct tevent_timer *te,
{
struct be_async_req *async_req;
}
struct be_spy {
struct be_spy *double_agent;
};
{
/* If there's a double_agent, set its
* freectx to NULL so that we don't
* try to loop. When that spy fires,
* it will just be a no-op.
*/
return 0;
}
{
/* Attach a spy for the be_client so that if it dies,
* we can free the be_req automatically.
*/
if (!cli_spy) {
goto done;
}
/* Also create a spy on the be_req so that we
* can free the other spy when the be_req
* completes successfully.
*/
if (!req_spy) {
goto done;
}
/* Create paired spy links to prevent loops */
/* Now create the destructors that will actually free
* the opposing spies.
*/
/* Now steal the be_req onto the mem_ctx so that it
* will be guaranteed that this data will be
* available for the full duration of execution.
*/
done:
}
return ret;
}
/* This function alters the memory hierarchy of the be_req
* to ensure memory safety during shutdown. It creates a
* spy on the be_cli object so that it will free the be_req
* if the client is freed.
*
* It is generally allocated atop the private data context
* for the appropriate back-end against which it is being
* filed.
*/
{
struct be_async_req *areq;
struct tevent_timer *te;
if (!areq) {
return ENOMEM;
}
/* fire immediately */
return EIO;
}
return EOK;
}
struct bet_queue_item **req_queue,
{
struct bet_queue_item *item;
int ret;
"running request immediately.\n"));
return ret;
}
}
"request queue.\n"));
} else {
}
return EOK;
}
{
struct bet_queue_item *item;
struct bet_queue_item **req_queue;
int ret;
const char *err_msg = "Cannot file back end request";
return;
}
break;
}
}
}
return;
}
return;
}
if (reply) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
if (!dbret) {
goto done;
}
goto done;
}
}
done:
}
{
/* check if we are past the offline blackout timeout */
/* FIXME: get offline_timeout from configuration */
}
}
{
ctx->run_online_cb = true;
}
{
}
{
switch (dp_err_type) {
case DP_ERR_OK:
break;
case DP_ERR_OFFLINE:
return talloc_asprintf(memctx,
"Provider is Offline (%s)",
break;
case DP_ERR_TIMEOUT:
return talloc_asprintf(memctx,
"Request timed out (%s)",
break;
case DP_ERR_FATAL:
default:
return talloc_asprintf(memctx,
"Internal Error (%s)",
break;
}
return NULL;
}
int dp_err_type,
int errnum,
const char *errstr)
{
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
if (reply) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
if (errstr) {
} else {
}
if (!err_msg) {
("Failed to set err_msg, Out of memory?\n"));
err_msg = "OOM";
}
if (!dbret) {
goto done;
}
goto done;
}
}
done:
}
{
struct be_subdom_req *req;
void *user_data;
bool force;
char *domain_hint;
const char *err_msg;
int ret;
if (!ret) {
return EIO;
}
/* return an error if corresponding backend target is not configured */
err_msg = "Subdomains back end target is not configured";
goto immediate;
}
/* If we are offline return immediately
*/
err_msg = "Provider is offline";
goto immediate;
}
/* process request */
if (!be_req) {
err_msg = "Out of memory";
goto immediate;
}
if (!req) {
err_msg = "Out of memory";
goto immediate;
}
if (!req->domain_hint) {
err_msg = "Out of memory";
goto immediate;
}
err_msg = "Cannot file back end request";
goto immediate;
}
return EOK;
if (be_req) {
}
if (reply) {
if (!dbret) {
return EIO;
}
}
/* send reply back */
}
return EOK;
}
int dp_err_type,
int errnum,
const char *errstr)
{
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
if (reply) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
if (errstr) {
} else {
}
if (!err_msg) {
err_msg = "OOM";
}
if (!dbret) {
return;
}
if (!dbus_conn) {
return;
}
}
/* finally free the request */
}
struct be_initgr_prereq {
char *user;
char *domain;
void *orig_pvt_data;
int orig_dp_err_type;
int orig_errnum;
const char *orig_errstr;
};
{
struct be_initgr_prereq);
}
{
}
int dp_err_type,
int errnum,
const char *errstr)
{
struct be_initgr_prereq);
int ret;
goto done;
}
/* Set up null request */
if (!msg) {
goto done;
}
if (!dbret) {
goto done;
}
/* ping the NSS service, no reply expected */
("Error contacting NSS responder: %d [%s]\n",
}
done:
if (msg) {
}
/* return immediately if we cannot contact nss provider */
}
}
{
struct be_acct_req);
struct be_initgr_prereq *pr;
struct ldb_result *res;
const char *tmpstr;
int i;
return ret;
}
/* if the user is completely missing there is no need to contact NSS,
* it would be a noop */
/* yet unknown, ignore */
return EOK;
}
if (!pr) {
return ENOMEM;
}
return ENOMEM;
}
if (!tmpstr) {
return EINVAL;
}
return ENOMEM;
}
return ENOMEM;
}
/* The first GID is the primary so it might be duplicated
* later in the list */
SYSDB_GIDNUM, 0);
/* if 0 it may be a non-posix group, so we skip it */
}
}
return EOK;
}
static errno_t
{
/* see if we need a pre request call, only done for initgroups for now */
if (ret) {
return ret;
}
}
/* process request */
return ret;
}
return EOK;
}
static errno_t
const char *filter,
char **name,
char **extended)
{
char *p;
if (!*name) {
return ENOENT;
}
if (p) {
/* Extended info included */
*p = '\0';
*extended = p + 1;
} else {
}
return EOK;
}
static void
const char *errstr);
struct be_get_account_info_state {
int err_maj;
int err_min;
const char *err_msg;
};
struct tevent_req *
struct tevent_context *ev,
struct be_acct_req *ar)
{
struct tevent_req *req;
struct be_get_account_info_state *state;
struct be_get_account_info_state);
if (!be_req) {
goto done;
}
goto done;
}
return req;
done:
return req;
}
static void
const char *errstr)
{
struct tevent_req *req;
struct be_get_account_info_state *state;
if (errstr) {
return;
}
}
}
int *_err_maj,
int *_err_min,
const char **_err_msg)
{
struct be_get_account_info_state *state;
if (_err_maj) {
}
if (_err_min) {
}
if (_err_msg) {
}
return EOK;
}
{
struct be_acct_req *req;
void *user_data;
char *filter;
char *domain;
int ret;
const char *err_msg;
if (!ret) {
return EIO;
}
/* If we are offline and fast reply was requested
* return offline immediately
*/
/* Send back an immediate reply */
err_msg = "Fast reply - offline";
/* This reply will be queued and sent
* when we reenter the mainloop.
*
* Continue processing in case we are
* going back online.
*/
}
if (!be_req) {
err_msg = "Out of memory";
goto done;
}
if (!req) {
err_msg = "Out of memory";
goto done;
}
err_msg = "Out of memory";
goto done;
}
if ((attr_type != BE_ATTR_CORE) &&
(attr_type != BE_ATTR_MEM) &&
(attr_type != BE_ATTR_ALL)) {
/* Unrecognized attr type */
err_msg = "Invalid Attrs Parameter";
goto done;
}
if (filter) {
&req->filter_value,
&req->extra_value);
&req->filter_value,
&req->extra_value);
&req->filter_value,
&req->extra_value);
} else {
err_msg = "Invalid Filter";
goto done;
}
err_msg = "Invalid Filter";
goto done;
}
} else {
err_msg = "Missing Filter Parameter";
goto done;
}
err_msg = "Cannot file account request";
goto done;
}
return EOK;
done:
if (be_req) {
}
if (reply) {
/* send reply back */
}
return EOK;
}
int dp_err_type,
int errnum,
const char *errstr)
{
dp_err_type == DP_ERR_OK) {
("SELinux provider doesn't exist, "
"not sending the request to it.\n"));
} else {
/* Now is the time to call SELinux provider */
req,
goto done;
}
return;
}
}
if (!dbret) {
goto done;
}
if (!dbus_conn) {
goto done;
}
done:
}
{
void *user_data;
if (!reply) {
return ENOMEM;
}
if (!be_req) {
return ENOMEM;
}
if (!ret) {
return EIO;
}
return ENOMEM;
}
}
case SSS_PAM_AUTHENTICATE:
break;
case SSS_PAM_ACCT_MGMT:
target = BET_ACCESS;
break;
case SSS_PAM_CHAUTHTOK:
case SSS_PAM_CHAUTHTOK_PRELIM:
target = BET_CHPASS;
break;
case SSS_PAM_OPEN_SESSION:
case SSS_PAM_SETCRED:
case SSS_PAM_CLOSE_SESSION:
goto done;
break;
default:
goto done;
}
/* return PAM_MODULE_UNKNOWN if corresponding backend target is not
* configured
*/
goto done;
}
goto done;
}
return EOK;
done:
if (!ret) {
return EIO;
}
/* send reply back immediately */
return EOK;
}
const char *errstr)
{
return;
}
if (!dbret) {
return;
}
if (!dbus_conn) {
return;
}
}
int dp_err,
int dp_ret,
const char *errstr)
{
}
{
int ret = 0;
int i;
return EINVAL;
}
return EINVAL;
}
if (!reply) {
("dbus_message_new_method_return failed, cannot send reply.\n"));
return ENOMEM;
}
/* create be request */
return ENOMEM;
}
/* get type of the request */
err_msg = "Invalid D-Bus message format";
goto fail;
}
/* If we are offline and fast reply was requested
* return offline immediately
*/
"Fast reply - offline");
/* This reply will be queued and sent
* when we reenter the mainloop.
*
* Continue processing in case we are
* going back online.
*/
}
/* get and set sudo request data */
goto fail;
}
/* get additional arguments according to the request type */
case BE_REQ_SUDO_FULL:
/* no arguments required */
break;
case BE_REQ_SUDO_RULES:
/* additional arguments:
* rules_num
* rules[rules_num]
*/
/* read rules_num */
err_msg = "Invalid D-Bus message format";
goto fail;
}
goto fail;
}
/* read the rules */
for (i = 0; i < rules_num; i++) {
err_msg = "Invalid D-Bus message format";
goto fail;
}
err_msg = "Invalid D-Bus message format";
goto fail;
}
goto fail;
}
}
break;
default:
err_msg = "Invalid DP request type";
goto fail;
}
/* return an error if corresponding backend target is not configured */
goto fail;
}
err_msg = "Cannot file back end request";
goto fail;
}
return EOK;
fail:
/* send reply back immediately */
return EOK;
}
int dp_err_type,
int errnum,
const char *errstr);
{
int ret = 0;
char *filter;
char *filter_val;
const char *err_msg;
return EINVAL;
}
return EINVAL;
}
if (!ret) {
return EIO;
}
if (!reply) {
("dbus_message_new_method_return failed, cannot send reply.\n"));
return ENOMEM;
}
/* If we are offline and fast reply was requested
* return offline immediately
*/
/* Send back an immediate reply */
err_msg = "Fast reply - offline";
/* This reply will be queued and sent
* when we reenter the mainloop.
*
* Continue processing in case we are
* going back online.
*/
}
if (filter) {
} else {
err_msg = "Invalid Filter";
goto done;
}
} else {
err_msg = "Missing Filter Parameter";
goto done;
}
/* create be request */
err_msg = "Out of memory";
goto done;
}
/* set autofs request data */
if (be_autofs_req == NULL) {
err_msg = "Out of memory";
goto done;
}
err_msg = "Out of memory";
goto done;
}
/* If a request for auto.master comes in, the automounter deamon
* has been reloaded. Expire all autofs maps to force reload
*/
be_autofs_req->invalidate = true;
}
err_msg = "Autofs back end target is not configured";
goto done;
}
err_msg = "Cannot file back end request";
goto done;
}
return EOK;
done:
if (be_req) {
}
if (reply) {
/* send reply back */
}
return EOK;
}
int dp_err_type,
int errnum,
const char *errstr)
{
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
if (reply) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
if (errstr) {
} else {
}
if (!err_msg) {
("Failed to set err_msg, Out of memory?\n"));
err_msg = "OOM";
}
if (!dbret) {
return;
}
if (!dbus_conn) {
return;
}
("Request processed. Returned %d,%d,%s\n",
}
/* finally free the request */
}
{
struct be_host_req *req;
void *user_data;
char *filter;
int ret;
const char *err_msg;
if (!ret) {
return EIO;
}
/* If we are offline and fast reply was requested
* return offline immediately
*/
/* Send back an immediate reply */
err_msg = "Fast reply - offline";
("Request processed. Returned %d,%d,%s\n",
/* This reply will be queued and sent
* when we reenter the mainloop.
*
* Continue processing in case we are
* going back online.
*/
}
if (!be_req) {
err_msg = "Out of memory";
goto done;
}
if (!req) {
err_msg = "Out of memory";
goto done;
}
if (filter) {
if (ret == 0) {
}
if (ret) {
err_msg = "Invalid Filter";
goto done;
}
} else {
err_msg = "Missing Filter Parameter";
goto done;
}
/* process request */
err_msg = "HostID back end target is not configured";
goto done;
}
err_msg = "Failed to file request";
goto done;
}
return EOK;
done:
if (be_req) {
}
if (reply) {
("Request processed. Returned %d,%d,%s\n",
/* send reply back */
}
return EOK;
}
static int be_client_destructor(void *ctx)
{
} else {
}
}
return 0;
}
struct sbus_connection *conn)
{
char *cli_name;
void *data;
if (!becli) {
DEBUG(0, ("Connection holds no valid init data\n"));
return EINVAL;
}
/* First thing, cancel the timeout */
if (!dbret) {
/* FIXME: should we just talloc_zfree(conn) ? */
return EIO;
}
} else {
}
/* reply that all is ok */
if (!reply) {
DEBUG(0, ("Dbus Out of memory!\n"));
return ENOMEM;
}
if (!dbret) {
DEBUG(0, ("Failed to build dbus reply\n"));
return EIO;
}
/* send reply back */
becli->initialized = true;
return EOK;
}
{
int ret;
}
return ret;
}
{
int ret;
goto done;
}
return;
}
done:
if (dp_err_type != DP_ERR_OFFLINE) {
if (dp_err_type != DP_ERR_OK) {
}
}
return;
}
{
int ret;
return;
}
/* Make sure nobody tries to go online while we are checking */
return;
}
goto failed;
}
goto failed;
}
goto failed;
}
return;
if (ctx->check_online_ref_count == 0) {
}
return;
}
struct tevent_timer *te,
{
}
{
/* hang off this memory to the connection so that when the connection
* is freed we can potentially call a destructor */
if (!becli) {
DEBUG(0,("Out of memory?!\n"));
return ENOMEM;
}
becli->initialized = false;
/* 5 seconds should be plenty */
DEBUG(0,("Out of memory?!\n"));
return ENOMEM;
}
/* Attach the client context to the connection context, so that it is
* always available when we need to manage the connection. */
return EOK;
}
/* be_srv_init
* set up per-domain sbus channel */
{
char *sbus_address;
int ret;
/* Set up SBUS connection to the monitor */
DEBUG(0, ("Could not get sbus backend address.\n"));
return ret;
}
DEBUG(0, ("Could not set up sbus server.\n"));
return ret;
}
return EOK;
}
{
}
static struct bet_ops be_target_access_permit_ops = {
.check_online = NULL,
};
{
}
static struct bet_ops be_target_access_deny_ops = {
.check_online = NULL,
};
const char *default_mod_name)
{
bool already_loaded = false;
int lb=0;
void *handle;
char *mod_init_fn_name = NULL;
return EINVAL;
}
if (!tmp_ctx) {
return ENOMEM;
}
&mod_name);
goto done;
}
if (!mod_name) {
if (default_mod_name != NULL) {
} else {
goto done;
}
}
goto done;
}
if (bet_type == BET_ACCESS) {
goto done;
}
goto done;
}
}
mod_name);
if (mod_init_fn_name == NULL) {
goto done;
}
lb = 0;
already_loaded = true;
break;
}
++lb;
goto done;
}
}
if (!already_loaded) {
if (!path) {
goto done;
}
if (!handle) {
DEBUG(0, ("Unable to load %s module with path (%s), error: %s\n",
goto done;
}
}
if (mod_init_fn == NULL) {
if (default_mod_name != NULL &&
/* If the default is used and fails we indicate this to the caller
* by returning ENOENT. Ths way the caller can decide how to
* handle the different types of error conditions. */
} else {
DEBUG(0, ("Unable to load init fn %s from module %s, error: %s\n",
}
goto done;
}
DEBUG(0, ("Error (%d) in module (%s) initialization (%s)!\n",
goto done;
}
done:
return ret;
}
struct tevent_signal *se,
int signum,
int count,
void *siginfo,
void *private_data)
{
}
{
bool responder_enabled = false;
int i;
int ret;
return ENOMEM;
}
goto done;
}
responder_enabled = true;
break;
}
}
goto done;
}
/* provider is not set explicitly */
("SUDO is not listed in services, disabling SUDO module.\n"));
goto done;
}
/* provider is set but responder is disabled */
"listed in active services. SUDO support will not work!\n"));
}
done:
return ret;
}
const char *be_domain,
struct tevent_context *ev,
struct confdb_ctx *cdb)
{
struct tevent_signal *tes;
int ret;
if (!ctx) {
DEBUG(0, ("fatal error initializing be_ctx\n"));
return ENOMEM;
}
DEBUG(0, ("Out of memory!?\n"));
goto fail;
}
("fatal error initializing failover context\n"));
goto fail;
}
goto fail;
}
("fatal error setting up monitor bus\n"));
goto fail;
}
/* We need this for subdomains support, as they have to store fully
* qualified user and group names for now */
("fatal error setting fully qualified name format for %s\n",
goto fail;
}
goto fail;
}
/* Initialize be_refresh periodic task. */
goto fail;
}
("Unable to initialize refresh periodic task\n"));
goto fail;
}
}
("fatal error initializing data providers\n"));
goto fail;
}
("ID backend target successfully loaded from provider [%s].\n",
("fatal error initializing data providers\n"));
goto fail;
}
("No authentication module provided for [%s] !!\n",
be_domain));
} else {
("AUTH backend target successfully loaded "
}
("Failed to setup ACCESS backend.\n"));
goto fail;
}
("ACCESS backend target successfully loaded "
("fatal error initializing data providers\n"));
goto fail;
}
("No change password module provided for [%s] !!\n",
be_domain));
} else {
("CHPASS backend target successfully loaded "
}
("fatal error initializing data providers\n"));
goto fail;
}
("No SUDO module provided for [%s] !!\n", be_domain));
} else {
("SUDO backend target successfully loaded "
}
("fatal error initializing data providers\n"));
goto fail;
}
("No autofs module provided for [%s] !!\n", be_domain));
} else {
("autofs backend target successfully loaded "
}
goto fail;
}
be_domain));
} else {
}
("fatal error initializing data providers\n"));
goto fail;
}
("No host info module provided for [%s] !!\n", be_domain));
} else {
("HOST backend target successfully loaded from provider [%s].\n",
}
} else {
"from provider [%s].\n",
}
/* Handle SIGUSR1 to force offline behavior */
BlockSignals(false, SIGUSR1);
goto fail;
}
return EOK;
fail:
return ret;
}
#ifndef UNIT_TESTING
{
int opt;
struct main_context *main_ctx;
char *confdb_path;
int ret;
struct poptOption long_options[] = {
_("Domain of the information provider (mandatory)"), NULL },
};
/* Set debug level to invalid value so we can deside if -d 0 was used. */
switch(opt) {
default:
return 1;
}
}
return 1;
}
/* set up things like debug , signals, daemonization, etc... */
if (!debug_log_file) return 2;
if (!srv_name) return 2;
if (!confdb_path) return 2;
return 2;
}
if (ret != 0) {
"logging mightnot work as expected\n"));
}
ret = die_if_parent_died();
/* This is not fatal, don't return */
}
return 3;
}
/* loop on main */
return 0;
}
#endif
struct sbus_connection *conn)
{
}
struct sbus_connection *conn)
{
}
struct sbus_connection *conn)
{
}
struct sbus_connection *conn)
{
struct be_ctx);
}