data_provider_be.c revision 5960687483a5d3d99093c9d6ab64e11c9bde7f7b
/*
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 mon_cli_iface monitor_be_methods = {
{ &mon_cli_iface_meta, 0 },
.clearMemcache = NULL,
.clearEnumCache = NULL,
.sysbusReconnect = NULL,
};
struct data_provider_iface be_methods = {
{ &data_provider_iface_meta, 0 },
};
};
#define REQ_PHASE_ACCESS 0
#define REQ_PHASE_SELINUX 1
struct be_req {
struct sss_domain_info *domain;
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 0;
}
{
/* Add this request to active request list and make sure it is
* removed on termination. */
return be_req;
}
{
return ERR_DOMAIN_NOT_FOUND;
}
return EOK;
}
{
}
{
}
{
}
const char *domain)
{
struct be_req *next_be_req;
domain);
return;
}
while (be_req) {
/* save pointer to next request in case be_req will be freed */
}
}
}
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;
struct sbus_request *dbus_req;
int ret;
const char *err_msg = "Cannot file back end request";
return;
}
break;
}
}
}
return;
}
return;
}
if (dbus_req) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
}
}
{
}
static errno_t
struct tevent_context *ev,
void *be_ctx_void)
{
return EOK;
}
{
int offline_timeout;
ctx->run_online_cb = true;
/* This is the first time we go offline - create a periodic task
* to check if we can switch to online. */
"Failed to get offline_timeout from confdb. "
"Will use 60 seconds.");
offline_timeout = 60;
}
3600 /* max_backoff */,
ctx, "Check if online (periodic)",
"be_ptask_create_sync failed [%d]: %s\n",
}
} else {
/* Periodic task was already created. Just enable it. */
}
}
{
}
{
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)
{
struct sbus_request *dbus_req;
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
if (dbus_req) {
/* 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";
}
}
}
{
struct be_subdom_req *req;
char *domain_hint;
const char *err_msg;
int ret;
return EOK; /* handled */
/* 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) {
}
/* send reply back */
}
return EOK;
}
int dp_err_type,
int errnum,
const char *errstr)
{
struct sbus_request *dbus_req;
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
if (dbus_req) {
/* 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";
}
}
/* 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 num;
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;
&res);
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;
}
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;
char *filter;
char *domain;
int ret;
const char *err_msg;
return EOK; /* handled */
/* If we are offline and fast reply was requested
* return offline immediately
*/
/* Send back an immediate reply */
err_msg = "Fast reply - offline";
return ret;
}
/* 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;
}
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 (dbus_req) {
return ret;
}
}
return EOK;
}
int dp_err_type,
int errnum,
const char *errstr)
{
struct sbus_request *dbus_req;
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;
}
}
"dbus_message_new_method_return failed, cannot send reply.\n");
goto done;
}
if (!dbret) {
goto done;
}
done:
}
{
if (!be_req) {
return ENOMEM;
}
if (!ret) {
return EIO;
}
return ENOMEM;
}
}
goto done;
}
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:
"dbus_message_new_method_return failed, cannot send reply.\n");
return ENOMEM;
}
if (!ret) {
return EIO;
}
/* send reply back immediately */
return EOK;
}
const char *errstr)
{
return;
}
}
int dp_err,
int dp_ret,
const char *errstr)
{
struct sbus_request *dbus_req;
}
{
int ret = 0;
int i;
return EINVAL;
}
/* 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;
}
err_msg = "Invalid D-Bus message format";
goto fail;
}
/* read the rules */
for (i = 0; i < rules_num; i++) {
!= DBUS_TYPE_STRING) {
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 EOK; /* handled */
/* If we are offline and fast reply was requested
* return offline immediately
*/
/* Send back an immediate reply */
err_msg = "Fast reply - offline";
return ret;
}
/* 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;
}
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 (dbus_req) {
return ret;
}
}
return EOK;
}
int dp_err_type,
int errnum,
const char *errstr)
{
struct sbus_request *dbus_req;
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
if (dbus_req) {
/* 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";
}
"Request processed. Returned %d,%d,%s\n",
}
/* finally free the request */
}
{
struct be_host_req *req;
char *filter;
int ret;
const char *err_msg;
return EOK; /* request finished */
/* If we are offline and fast reply was requested
* return offline immediately
*/
/* Send back an immediate reply */
err_msg = "Fast reply - offline";
return ret;
}
"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 (dbus_req) {
return ret;
}
"Request processed. Returned %d,%d,%s\n",
}
return EOK;
}
static int be_client_destructor(void *ctx)
{
} else {
}
}
return 0;
}
{
struct sbus_connection *conn;
char *cli_name;
int ret;
if (!becli) {
return EINVAL;
}
/* First thing, cancel the timeout */
if (!dbret) {
"Failed to parse message, killing connection\n");
/* FIXME: should we just talloc_zfree(conn) ? */
return EIO;
}
} else {
}
/* reply that all is ok */
return ret;
}
becli->initialized = true;
return EOK;
}
{
int ret;
}
return ret;
}
{
int ret;
"be_file_check_online_request failed.\n");
goto done;
}
return;
}
done:
if (dp_err_type != DP_ERR_OFFLINE) {
if (dp_err_type != DP_ERR_OK) {
}
}
return;
}
{
int ret;
"Backend is already online, nothing to do.\n");
return;
}
/* Make sure nobody tries to go online while we are checking */
"There is an online check already running.\n");
return;
}
"ID providers does not provide a check_online method.\n");
goto failed;
}
goto failed;
}
goto failed;
}
return;
if (ctx->check_online_ref_count == 0) {
}
return;
}
struct tevent_timer *te,
{
"Client timed out before Identification [%p]!\n", te);
}
{
struct sbus_interface *intf;
/* hang off this memory to the connection so that when the connection
* is freed we can potentially call a destructor */
if (!becli) {
return ENOMEM;
}
becli->initialized = false;
/* 5 seconds should be plenty */
return ENOMEM;
}
/* Attach the client context to the connection context, so that it is
* always available when we need to manage the connection. */
if (!intf) {
return ENOMEM;
}
}
/* be_srv_init
* set up per-domain sbus channel */
{
char *sbus_address;
int ret;
/* Set up SBUS connection to the monitor */
return ret;
}
return ret;
}
return EOK;
}
{
"be_target_access_permit called, returning PAM_SUCCESS.\n");
}
static struct bet_ops be_target_access_permit_ops = {
.check_online = NULL,
};
{
"be_target_access_deny called, returning PAM_PERM_DENIED.\n");
}
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) {
"no module name found in confdb, using [%s].\n",
} 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;
"Backend [%s] already loaded.\n", mod_name);
already_loaded = true;
break;
}
++lb;
goto done;
}
}
if (!already_loaded) {
if (!path) {
goto done;
}
if (!handle) {
"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 {
"Unable to load init fn %s from module %s, error: %s\n",
}
goto done;
}
"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)
{
}
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) {
return ENOMEM;
}
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",
} 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",
} 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;
}
} 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;
}
/* Handle SIGUSR2 to force going online */
BlockSignals(false, SIGUSR2);
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 */
"Could not set up to exit when parent process does\n");
}
return 3;
}
/* loop on main */
return 0;
}
#endif
{
}
{
}
{
}
{
}