data_provider_be.c revision 92eb5729e132e4f88ec41f1eb8db90ebd3a81a63
/*
SSSD
Data Provider Process
Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
This program is free software; you can redistribute it and/or modify
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 <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
#include <dlfcn.h>
#include <popt.h>
#include <dbus/dbus.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include "util/util.h"
#include "confdb/confdb.h"
#include "db/sysdb.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"
static int data_provider_res_init(DBusMessage *message,
struct sbus_connection *conn);
static int data_provider_go_offline(DBusMessage *message,
struct sbus_connection *conn);
static int data_provider_reset_offline(DBusMessage *message,
struct sbus_connection *conn);
static int data_provider_logrotate(DBusMessage *message,
struct sbus_connection *conn);
struct sbus_method monitor_be_methods[] = {
{ MON_CLI_METHOD_PING, monitor_common_pong },
{ MON_CLI_METHOD_RES_INIT, data_provider_res_init },
{ MON_CLI_METHOD_OFFLINE, data_provider_go_offline },
{ MON_CLI_METHOD_RESET_OFFLINE, data_provider_reset_offline },
{ MON_CLI_METHOD_ROTATE, data_provider_logrotate },
{ NULL, NULL }
};
struct sbus_interface monitor_be_interface = {
MONITOR_INTERFACE,
MONITOR_PATH,
SBUS_DEFAULT_VTABLE,
monitor_be_methods,
NULL
};
static int client_registration(DBusMessage *message, struct sbus_connection *conn);
static int be_get_account_info(DBusMessage *message, struct sbus_connection *conn);
static int be_pam_handler(DBusMessage *message, struct sbus_connection *conn);
static int be_sudo_handler(DBusMessage *message, struct sbus_connection *conn);
static int be_autofs_handler(DBusMessage *message, struct sbus_connection *conn);
static int be_host_handler(DBusMessage *message, struct sbus_connection *conn);
static int be_get_subdomains(DBusMessage *message, struct sbus_connection *conn);
struct sbus_method be_methods[] = {
{ DP_METHOD_REGISTER, client_registration },
{ DP_METHOD_GETACCTINFO, be_get_account_info },
{ DP_METHOD_PAMHANDLER, be_pam_handler },
{ DP_METHOD_SUDOHANDLER, be_sudo_handler },
{ DP_METHOD_AUTOFSHANDLER, be_autofs_handler },
{ DP_METHOD_HOSTHANDLER, be_host_handler },
{ DP_METHOD_GETDOMAINS, be_get_subdomains },
{ NULL, NULL }
};
struct sbus_interface be_interface = {
DP_INTERFACE,
DP_PATH,
SBUS_DEFAULT_VTABLE,
be_methods,
NULL
};
static struct bet_data bet_data[] = {
{BET_NULL, NULL, NULL},
{BET_ID, CONFDB_DOMAIN_ID_PROVIDER, "sssm_%s_id_init"},
{BET_AUTH, CONFDB_DOMAIN_AUTH_PROVIDER, "sssm_%s_auth_init"},
{BET_ACCESS, CONFDB_DOMAIN_ACCESS_PROVIDER, "sssm_%s_access_init"},
{BET_CHPASS, CONFDB_DOMAIN_CHPASS_PROVIDER, "sssm_%s_chpass_init"},
{BET_SUDO, CONFDB_DOMAIN_SUDO_PROVIDER, "sssm_%s_sudo_init"},
{BET_AUTOFS, CONFDB_DOMAIN_AUTOFS_PROVIDER, "sssm_%s_autofs_init"},
{BET_SELINUX, CONFDB_DOMAIN_SELINUX_PROVIDER, "sssm_%s_selinux_init"},
{BET_HOSTID, CONFDB_DOMAIN_HOSTID_PROVIDER, "sssm_%s_hostid_init"},
{BET_SUBDOMAINS, CONFDB_DOMAIN_SUBDOMAINS_PROVIDER, "sssm_%s_subdomains_init"},
{BET_MAX, NULL, NULL}
};
#define REQ_PHASE_ACCESS 0
#define REQ_PHASE_SELINUX 1
struct be_req {
struct be_client *becli;
struct be_ctx *be_ctx;
void *req_data;
be_async_callback_t fn;
void *pvt;
/* This is utilized in access provider
* request handling to indicate if access or
* selinux provider is calling the callback.
*/
int phase;
struct be_req *prev;
struct be_req *next;
};
static int be_req_destructor(struct be_req *be_req)
{
DLIST_REMOVE(be_req->be_ctx->active_requests, be_req);
return 0;
}
struct be_req *be_req_create(TALLOC_CTX *mem_ctx,
struct be_client *becli, struct be_ctx *be_ctx,
be_async_callback_t fn, void *pvt_fn_data)
{
struct be_req *be_req;
be_req = talloc_zero(mem_ctx, struct be_req);
if (be_req == NULL) return NULL;
be_req->becli = becli;
be_req->be_ctx = be_ctx;
be_req->fn = fn;
be_req->pvt = pvt_fn_data;
/* Add this request to active request list and make sure it is
* removed on termination. */
DLIST_ADD(be_ctx->active_requests, be_req);
talloc_set_destructor(be_req, be_req_destructor);
return be_req;
}
struct be_ctx *be_req_get_be_ctx(struct be_req *be_req)
{
return be_req->be_ctx;
}
void *be_req_get_data(struct be_req *be_req)
{
return be_req->req_data;
}
void be_req_terminate(struct be_req *be_req,
int dp_err_type, int errnum, const char *errstr)
{
if (be_req->fn == NULL) return;
be_req->fn(be_req, dp_err_type, errnum, errstr);
}
struct be_async_req {
be_req_fn_t fn;
struct be_req *req;
};
static void be_async_req_handler(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval tv, void *pvt)
{
struct be_async_req *async_req;
async_req = talloc_get_type(pvt, struct be_async_req);
async_req->fn(async_req->req);
}
struct be_spy {
TALLOC_CTX *freectx;
struct be_spy *double_agent;
};
static int be_spy_destructor(struct be_spy *spy)
{
/* 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.
*/
spy->double_agent->freectx = NULL;
talloc_zfree(spy->freectx);
return 0;
}
static errno_t be_spy_create(TALLOC_CTX *mem_ctx, struct be_req *be_req)
{
errno_t ret;
struct be_spy *cli_spy = NULL;
struct be_spy *req_spy = NULL;
/* Attach a spy for the be_client so that if it dies,
* we can free the be_req automatically.
*/
cli_spy = talloc_zero(be_req->becli, struct be_spy);
if (!cli_spy) {
ret = ENOMEM;
goto done;
}
cli_spy->freectx = mem_ctx;
/* Also create a spy on the be_req so that we
* can free the other spy when the be_req
* completes successfully.
*/
req_spy = talloc_zero(be_req, struct be_spy);
if (!req_spy) {
ret = ENOMEM;
goto done;
}
req_spy->freectx = cli_spy;
/* Create paired spy links to prevent loops */
cli_spy->double_agent = req_spy;
req_spy->double_agent = cli_spy;
/* Now create the destructors that will actually free
* the opposing spies.
*/
talloc_set_destructor(cli_spy, be_spy_destructor);
talloc_set_destructor(req_spy, be_spy_destructor);
/* 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.
*/
talloc_steal(mem_ctx, be_req);
ret = EOK;
done:
if (ret != EOK) {
talloc_free(cli_spy);
talloc_free(req_spy);
}
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.
*/
static errno_t be_file_request(TALLOC_CTX *mem_ctx,
struct be_req *be_req,
be_req_fn_t fn)
{
errno_t ret;
struct be_async_req *areq;
struct tevent_timer *te;
struct timeval tv;
if (!fn || !be_req) return EINVAL;
ret = be_spy_create(mem_ctx, be_req);
if (ret != EOK) return ret;
areq = talloc(be_req, struct be_async_req);
if (!areq) {
return ENOMEM;
}
areq->fn = fn;
areq->req = be_req;
/* fire immediately */
tv.tv_sec = 0;
tv.tv_usec = 0;
te = tevent_add_timer(be_req->be_ctx->ev, be_req,
tv, be_async_req_handler, areq);
if (te == NULL) {
return EIO;
}
return EOK;
}
static errno_t be_queue_request(TALLOC_CTX *queue_mem_ctx,
struct bet_queue_item **req_queue,
TALLOC_CTX *req_mem_ctx,
struct be_req *be_req,
be_req_fn_t fn)
{
struct bet_queue_item *item;
int ret;
if (*req_queue == NULL) {
DEBUG(SSSDBG_TRACE_ALL, ("Queue is empty, " \
"running request immediately.\n"));
ret = be_file_request(req_mem_ctx, be_req, fn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("be_file_request failed.\n"));
return ret;
}
}
item = talloc_zero(queue_mem_ctx, struct bet_queue_item);
if (item == NULL) {
DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed, cannot add item to " \
"request queue.\n"));
} else {
DEBUG(SSSDBG_TRACE_ALL, ("Adding request to queue.\n"));
item->mem_ctx = req_mem_ctx;
item->be_req = be_req;
item->fn = fn;
DLIST_ADD_END(*req_queue, item, struct bet_queue_item *);
}
return EOK;
}
static void be_queue_next_request(struct be_req *be_req, enum bet_type type)
{
struct bet_queue_item *item;
struct bet_queue_item *current = NULL;
struct bet_queue_item **req_queue;
int ret;
DBusMessage *reply;
uint16_t err_maj;
uint32_t err_min;
const char *err_msg = "Cannot file back end request";
struct be_req *next_be_req = NULL;
dbus_bool_t dbret;
DBusConnection *dbus_conn;
req_queue = &be_req->becli->bectx->bet_info[type].req_queue;
if (*req_queue == NULL) {
DEBUG(SSSDBG_TRACE_ALL, ("Queue is empty, nothing to do.\n"));
return;
}
DLIST_FOR_EACH(item, *req_queue) {
if (item->be_req == be_req) {
current = item;
break;
}
}
if (current != NULL) {
DLIST_REMOVE(*req_queue, current);
}
if (*req_queue == NULL) {
DEBUG(SSSDBG_TRACE_ALL, ("Request queue is empty.\n"));
return;
}
next_be_req = (*req_queue)->be_req;
ret = be_file_request((*req_queue)->mem_ctx, next_be_req, (*req_queue)->fn);
if (ret == EOK) {
DEBUG(SSSDBG_TRACE_ALL, ("Queued request filed successfully.\n"));
return;
}
DEBUG(SSSDBG_OP_FAILURE, ("be_file_request failed.\n"));
be_queue_next_request(next_be_req, type);
reply = (DBusMessage *) next_be_req->pvt;
if (reply) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
err_maj = DP_ERR_FATAL;
err_min = ret;
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to generate dbus reply\n"));
dbus_message_unref(reply);
goto done;
}
dbus_conn = sbus_get_connection(next_be_req->becli->conn);
if (dbus_conn == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("D-BUS not connected\n"));
goto done;
}
dbus_connection_send(dbus_conn, reply, NULL);
dbus_message_unref(reply);
}
done:
talloc_free(next_be_req);
}
bool be_is_offline(struct be_ctx *ctx)
{
time_t now = time(NULL);
/* check if we are past the offline blackout timeout */
/* FIXME: get offline_timeout from configuration */
if (ctx->offstat.went_offline + 60 < now) {
ctx->offstat.offline = false;
}
return ctx->offstat.offline;
}
void be_mark_offline(struct be_ctx *ctx)
{
DEBUG(8, ("Going offline!\n"));
ctx->offstat.went_offline = time(NULL);
ctx->offstat.offline = true;
ctx->run_online_cb = true;
be_run_offline_cb(ctx);
}
static void be_reset_offline(struct be_ctx *ctx)
{
ctx->offstat.went_offline = 0;
ctx->offstat.offline = false;
be_run_online_cb(ctx);
}
static char *dp_pam_err_to_string(TALLOC_CTX *memctx, int dp_err_type, int errnum)
{
switch (dp_err_type) {
case DP_ERR_OK:
return talloc_strdup(memctx, "Success");
break;
case DP_ERR_OFFLINE:
return talloc_asprintf(memctx,
"Provider is Offline (%s)",
pam_strerror(NULL, errnum));
break;
case DP_ERR_TIMEOUT:
return talloc_asprintf(memctx,
"Request timed out (%s)",
pam_strerror(NULL, errnum));
break;
case DP_ERR_FATAL:
default:
return talloc_asprintf(memctx,
"Internal Error (%s)",
pam_strerror(NULL, errnum));
break;
}
return NULL;
}
static void get_subdomains_callback(struct be_req *req,
int dp_err_type,
int errnum,
const char *errstr)
{
DBusMessage *reply;
DBusConnection *dbus_conn;
dbus_bool_t dbret;
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
const char *err_msg = NULL;
DEBUG(SSSDBG_TRACE_FUNC, ("Backend returned: (%d, %d, %s) [%s]\n",
dp_err_type, errnum, errstr?errstr:"<NULL>",
dp_pam_err_to_string(req, dp_err_type, errnum)));
be_queue_next_request(req, BET_SUBDOMAINS);
reply = (DBusMessage *)req->pvt;
if (reply) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
err_maj = dp_err_type;
err_min = errnum;
if (errstr) {
err_msg = errstr;
} else {
err_msg = dp_pam_err_to_string(req, dp_err_type, errnum);
}
if (!err_msg) {
DEBUG(SSSDBG_CRIT_FAILURE,
("Failed to set err_msg, Out of memory?\n"));
err_msg = "OOM";
}
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to generate dbus reply\n"));
dbus_message_unref(reply);
goto done;
}
dbus_conn = sbus_get_connection(req->becli->conn);
if (dbus_conn == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("D-BUS not connected\n"));
goto done;
}
dbus_connection_send(dbus_conn, reply, NULL);
dbus_message_unref(reply);
}
done:
talloc_free(req);
}
static int be_get_subdomains(DBusMessage *message, struct sbus_connection *conn)
{
struct be_subdom_req *req;
struct be_req *be_req = NULL;
struct be_client *becli;
DBusMessage *reply;
DBusError dbus_error;
dbus_bool_t dbret;
void *user_data;
bool force;
char *domain_hint;
dbus_uint16_t err_maj;
dbus_uint32_t err_min;
const char *err_msg;
int ret;
user_data = sbus_conn_get_private_data(conn);
if (!user_data) return EINVAL;
becli = talloc_get_type(user_data, struct be_client);
if (!becli) return EINVAL;
dbus_error_init(&dbus_error);
ret = dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_BOOLEAN, &force,
DBUS_TYPE_STRING, &domain_hint,
DBUS_TYPE_INVALID);
if (!ret) {
DEBUG(SSSDBG_CRIT_FAILURE,("Failed, to parse message!\n"));
if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
return EIO;
}
reply = dbus_message_new_method_return(message);
if (!reply) return ENOMEM;
/* return an error if corresponding backend target is not configured */
if (becli->bectx->bet_info[BET_SUBDOMAINS].bet_ops == NULL) {
DEBUG(SSSDBG_TRACE_INTERNAL, ("Undefined backend target.\n"));
err_maj = DP_ERR_FATAL;
err_min = ENODEV;
err_msg = "Subdomains back end target is not configured";
goto immediate;
}
DEBUG(SSSDBG_TRACE_FUNC,
("Got get subdomains [%sforced][%s]\n", force ? "" : "not ",
domain_hint == NULL ? "no hint": domain_hint ));
/* If we are offline return immediately
*/
if (becli->bectx->offstat.offline) {
DEBUG(SSSDBG_TRACE_FUNC, ("Cannot proceed, provider is offline.\n"));
err_maj = DP_ERR_OFFLINE;
err_min = EAGAIN;
err_msg = "Provider is offline";
goto immediate;
}
/* process request */
be_req = be_req_create(becli, becli, becli->bectx,
get_subdomains_callback, reply);
if (!be_req) {
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto immediate;
}
req = talloc(be_req, struct be_subdom_req);
if (!req) {
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto immediate;
}
req->force = force;
req->domain_hint = talloc_strdup(req, domain_hint);
if (!req->domain_hint) {
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto immediate;
}
be_req->req_data = req;
ret = be_queue_request(becli->bectx,
&becli->bectx->bet_info[BET_SUBDOMAINS].req_queue,
becli->bectx,
be_req,
becli->bectx->bet_info[BET_SUBDOMAINS].bet_ops->handler);
if (ret != EOK) {
err_maj = DP_ERR_FATAL;
err_min = ret;
err_msg = "Cannot file back end request";
goto immediate;
}
return EOK;
immediate:
if (be_req) {
talloc_free(be_req);
}
if (reply) {
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to generate dbus reply\n"));
dbus_message_unref(reply);
return EIO;
}
if (!(err_maj == DP_ERR_FATAL && err_min == ENODEV)) {
DEBUG(SSSDBG_TRACE_LIBS, ("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
}
/* send reply back */
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
}
return EOK;
}
static void acctinfo_callback(struct be_req *req,
int dp_err_type,
int errnum,
const char *errstr)
{
DBusMessage *reply;
DBusConnection *dbus_conn;
dbus_bool_t dbret;
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
const char *err_msg = NULL;
reply = (DBusMessage *)req->pvt;
if (reply) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
err_maj = dp_err_type;
err_min = errnum;
if (errstr) {
err_msg = errstr;
} else {
err_msg = dp_pam_err_to_string(req, dp_err_type, errnum);
}
if (!err_msg) {
DEBUG(1, ("Failed to set err_msg, Out of memory?\n"));
err_msg = "OOM";
}
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(1, ("Failed to generate dbus reply\n"));
return;
}
dbus_conn = sbus_get_connection(req->becli->conn);
if (!dbus_conn) {
DEBUG(SSSDBG_CRIT_FAILURE, ("D-BUS not connected\n"));
return;
}
dbus_connection_send(dbus_conn, reply, NULL);
dbus_message_unref(reply);
DEBUG(4, ("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
}
/* finally free the request */
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_get_data(be_req),
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->domain->sysdb,
be_req->be_ctx->domain, ar->filter_value, &res);
if (ret && ret != ENOENT) {
return ret;
}
/* if the user is completely missing there is no need to contact NSS,
* it would be a noop */
if (ret == ENOENT || res->count == 0) {
/* 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);
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;
}
/* The first GID is the primary so it might be duplicated
* later in the list */
for (pr->gnum = 0, i = 0; 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
be_file_account_request(struct be_req *be_req, struct be_acct_req *ar)
{
errno_t ret;
struct be_ctx *be_ctx = be_req->be_ctx;
be_req->req_data = ar;
/* see if we need a pre request call, only done for initgroups for now */
if ((ar->entry_type & 0xFF) == BE_REQ_INITGROUPS) {
ret = be_initgroups_prereq(be_req);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Prerequest failed"));
return ret;
}
}
/* process request */
ret = be_file_request(be_ctx, be_req,
be_ctx->bet_info[BET_ID].bet_ops->handler);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to file request"));
return ret;
}
return EOK;
}
static errno_t
split_name_extended(TALLOC_CTX *mem_ctx,
const char *filter,
char **name,
char **extended)
{
char *p;
*name = talloc_strdup(mem_ctx, filter);
if (!*name) {
return ENOENT;
}
p = strchr(*name, ':');
if (p) {
/* Extended info included */
*p = '\0';
*extended = p + 1;
} else {
*extended = NULL;
}
return EOK;
}
static void
be_get_account_info_done(struct be_req *be_req,
int dp_err, int dp_ret,
const char *errstr);
struct be_get_account_info_state {
int err_maj;
int err_min;
const char *err_msg;
};
struct tevent_req *
be_get_account_info_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct be_client *becli,
struct be_ctx *be_ctx,
struct be_acct_req *ar)
{
struct tevent_req *req;
struct be_get_account_info_state *state;
struct be_req *be_req;
errno_t ret;
req = tevent_req_create(mem_ctx, &state,
struct be_get_account_info_state);
if (!req) return NULL;
be_req = be_req_create(state, becli, be_ctx,
be_get_account_info_done, req);
if (!be_req) {
ret = ENOMEM;
goto done;
}
ret = be_file_account_request(be_req, ar);
if (ret != EOK) {
goto done;
}
return req;
done:
tevent_req_error(req, ret);
tevent_req_post(req, ev);
return req;
}
static void
be_get_account_info_done(struct be_req *be_req,
int dp_err, int dp_ret,
const char *errstr)
{
struct tevent_req *req;
struct be_get_account_info_state *state;
req = talloc_get_type(be_req->pvt, struct tevent_req);
state = tevent_req_data(req, struct be_get_account_info_state);
state->err_maj = dp_err;
state->err_min = dp_ret;
if (errstr) {
state->err_msg = talloc_strdup(state, errstr);
if (state->err_msg == NULL) {
talloc_free(be_req);
tevent_req_error(req, ENOMEM);
return;
}
}
talloc_free(be_req);
tevent_req_done(req);
}
errno_t be_get_account_info_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
int *_err_maj,
int *_err_min,
const char **_err_msg)
{
struct be_get_account_info_state *state;
state = tevent_req_data(req, struct be_get_account_info_state);
TEVENT_REQ_RETURN_ON_ERROR(req);
if (_err_maj) {
*_err_maj = state->err_maj;
}
if (_err_min) {
*_err_min = state->err_min;
}
if (_err_msg) {
*_err_msg = talloc_steal(mem_ctx, state->err_msg);
}
return EOK;
}
static int be_get_account_info(DBusMessage *message, struct sbus_connection *conn)
{
struct be_acct_req *req;
struct be_req *be_req;
struct be_client *becli;
DBusMessage *reply;
DBusError dbus_error;
dbus_bool_t dbret;
void *user_data;
uint32_t type;
char *filter;
char *domain;
uint32_t attr_type;
int ret;
dbus_uint16_t err_maj;
dbus_uint32_t err_min;
const char *err_msg;
be_req = NULL;
user_data = sbus_conn_get_private_data(conn);
if (!user_data) return EINVAL;
becli = talloc_get_type(user_data, struct be_client);
if (!becli) return EINVAL;
dbus_error_init(&dbus_error);
ret = dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_UINT32, &type,
DBUS_TYPE_UINT32, &attr_type,
DBUS_TYPE_STRING, &filter,
DBUS_TYPE_STRING, &domain,
DBUS_TYPE_INVALID);
if (!ret) {
DEBUG(1,("Failed, to parse message!\n"));
if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
return EIO;
}
DEBUG(4, ("Got request for [%u][%d][%s]\n", type, attr_type, filter));
reply = dbus_message_new_method_return(message);
if (!reply) return ENOMEM;
/* If we are offline and fast reply was requested
* return offline immediately
*/
if ((type & BE_REQ_FAST) && becli->bectx->offstat.offline) {
/* Send back an immediate reply */
err_maj = DP_ERR_OFFLINE;
err_min = EAGAIN;
err_msg = "Fast reply - offline";
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) return EIO;
DEBUG(4, ("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
reply = NULL;
/* This reply will be queued and sent
* when we reenter the mainloop.
*
* Continue processing in case we are
* going back online.
*/
}
be_req = be_req_create(becli, becli, becli->bectx,
acctinfo_callback, reply);
if (!be_req) {
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto done;
}
req = talloc(be_req, struct be_acct_req);
if (!req) {
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto done;
}
req->entry_type = type;
req->attr_type = (int)attr_type;
req->domain = talloc_strdup(req, domain);
if (!req->domain) {
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
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_maj = DP_ERR_FATAL;
err_min = EINVAL;
err_msg = "Invalid Attrs Parameter";
goto done;
}
if (filter) {
ret = EOK;
if (strncmp(filter, "name=", 5) == 0) {
req->filter_type = BE_FILTER_NAME;
ret = split_name_extended(req, &filter[5],
&req->filter_value,
&req->extra_value);
} else if (strncmp(filter, "idnumber=", 9) == 0) {
req->filter_type = BE_FILTER_IDNUM;
ret = split_name_extended(req, &filter[9],
&req->filter_value,
&req->extra_value);
} else if (strncmp(filter, DP_SEC_ID"=", DP_SEC_ID_LEN + 1) == 0) {
req->filter_type = BE_FILTER_SECID;
ret = split_name_extended(req, &filter[DP_SEC_ID_LEN + 1],
&req->filter_value,
&req->extra_value);
} else if (strcmp(filter, ENUM_INDICATOR) == 0) {
req->filter_type = BE_FILTER_ENUM;
req->filter_value = NULL;
} else {
err_maj = DP_ERR_FATAL;
err_min = EINVAL;
err_msg = "Invalid Filter";
goto done;
}
if (ret != EOK) {
err_maj = DP_ERR_FATAL;
err_min = EINVAL;
err_msg = "Invalid Filter";
goto done;
}
} else {
err_maj = DP_ERR_FATAL;
err_min = EINVAL;
err_msg = "Missing Filter Parameter";
goto done;
}
ret = be_file_account_request(be_req, req);
if (ret != EOK) {
err_maj = DP_ERR_FATAL;
err_min = ret;
err_msg = "Cannot file account request";
goto done;
}
return EOK;
done:
if (be_req) {
talloc_free(be_req);
}
if (reply) {
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) return EIO;
DEBUG(4, ("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
/* send reply back */
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
}
return EOK;
}
static void be_pam_handler_callback(struct be_req *req,
int dp_err_type,
int errnum,
const char *errstr)
{
struct be_client *becli = req->becli;
struct pam_data *pd;
DBusMessage *reply;
DBusConnection *dbus_conn;
dbus_bool_t dbret;
errno_t ret;
DEBUG(4, ("Backend returned: (%d, %d, %s) [%s]\n",
dp_err_type, errnum, errstr?errstr:"<NULL>",
dp_pam_err_to_string(req, dp_err_type, errnum)));
pd = talloc_get_type(be_req_get_data(req), struct pam_data);
if (pd->cmd == SSS_PAM_ACCT_MGMT &&
pd->pam_status == PAM_SUCCESS &&
req->phase == REQ_PHASE_ACCESS &&
dp_err_type == DP_ERR_OK) {
if (!becli->bectx->bet_info[BET_SELINUX].bet_ops) {
DEBUG(SSSDBG_TRACE_FUNC,
("SELinux provider doesn't exist, "
"not sending the request to it.\n"));
} else {
req->phase = REQ_PHASE_SELINUX;
/* Now is the time to call SELinux provider */
ret = be_file_request(becli->bectx->bet_info[BET_SELINUX].pvt_bet_data,
req,
becli->bectx->bet_info[BET_SELINUX].bet_ops->handler);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("be_file_request failed.\n"));
goto done;
}
return;
}
}
DEBUG(4, ("Sending result [%d][%s]\n", pd->pam_status, pd->domain));
reply = (DBusMessage *)req->pvt;
dbret = dp_pack_pam_response(reply, pd);
if (!dbret) {
DEBUG(1, ("Failed to generate dbus reply\n"));
dbus_message_unref(reply);
goto done;
}
dbus_conn = sbus_get_connection(req->becli->conn);
if (!dbus_conn) {
DEBUG(SSSDBG_CRIT_FAILURE, ("D-BUS not connected\n"));
goto done;
}
dbus_connection_send(dbus_conn, reply, NULL);
dbus_message_unref(reply);
DEBUG(4, ("Sent result [%d][%s]\n", pd->pam_status, pd->domain));
done:
talloc_free(req);
}
static int be_pam_handler(DBusMessage *message, struct sbus_connection *conn)
{
DBusError dbus_error;
DBusMessage *reply;
struct be_client *becli;
dbus_bool_t ret;
void *user_data;
struct pam_data *pd = NULL;
struct be_req *be_req = NULL;
enum bet_type target = BET_NULL;
user_data = sbus_conn_get_private_data(conn);
if (!user_data) return EINVAL;
becli = talloc_get_type(user_data, struct be_client);
if (!becli) return EINVAL;
reply = dbus_message_new_method_return(message);
if (!reply) {
DEBUG(1, ("dbus_message_new_method_return failed, cannot send reply.\n"));
return ENOMEM;
}
be_req = be_req_create(becli, becli, becli->bectx,
be_pam_handler_callback, reply);
if (!be_req) {
DEBUG(7, ("talloc_zero failed.\n"));
dbus_message_unref(reply);
return ENOMEM;
}
dbus_error_init(&dbus_error);
ret = dp_unpack_pam_request(message, be_req, &pd, &dbus_error);
if (!ret) {
DEBUG(1,("Failed, to parse message!\n"));
talloc_free(be_req);
return EIO;
}
pd->pam_status = PAM_SYSTEM_ERR;
if (pd->domain == NULL) {
pd->domain = talloc_strdup(pd, becli->bectx->domain->name);
if (pd->domain == NULL) {
talloc_free(be_req);
return ENOMEM;
}
}
DEBUG(4, ("Got request with the following data\n"));
DEBUG_PAM_DATA(4, pd);
switch (pd->cmd) {
case SSS_PAM_AUTHENTICATE:
target = BET_AUTH;
break;
case SSS_PAM_ACCT_MGMT:
target = BET_ACCESS;
be_req->phase = REQ_PHASE_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:
pd->pam_status = PAM_SUCCESS;
goto done;
break;
default:
DEBUG(7, ("Unsupported PAM command [%d].\n", pd->cmd));
pd->pam_status = PAM_MODULE_UNKNOWN;
goto done;
}
/* return PAM_MODULE_UNKNOWN if corresponding backend target is not
* configured
*/
if (!becli->bectx->bet_info[target].bet_ops) {
DEBUG(7, ("Undefined backend target.\n"));
pd->pam_status = PAM_MODULE_UNKNOWN;
goto done;
}
be_req->req_data = pd;
ret = be_file_request(becli->bectx->bet_info[target].pvt_bet_data,
be_req,
becli->bectx->bet_info[target].bet_ops->handler);
if (ret != EOK) {
DEBUG(7, ("be_file_request failed.\n"));
goto done;
}
return EOK;
done:
DEBUG(4, ("Sending result [%d][%s]\n",
pd->pam_status, pd->domain));
ret = dp_pack_pam_response(reply, pd);
if (!ret) {
DEBUG(1, ("Failed to generate dbus reply\n"));
talloc_free(be_req);
dbus_message_unref(reply);
return EIO;
}
/* send reply back immediately */
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
talloc_free(be_req);
return EOK;
}
static void be_sudo_handler_reply(struct sbus_connection *conn,
DBusMessage *reply,
dbus_uint16_t dp_err,
dbus_uint32_t dp_ret,
const char *errstr)
{
DBusConnection *dbus_conn = NULL;
dbus_bool_t dbret;
const char *err_msg = NULL;
if (reply == NULL) {
return;
}
err_msg = errstr ? errstr : "No errmsg set\n";
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &dp_err,
DBUS_TYPE_UINT32, &dp_ret,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to generate dbus reply\n"));
return;
}
DEBUG(SSSDBG_FUNC_DATA, ("SUDO Backend returned: (%d, %d, %s)\n",
dp_err, dp_ret, errstr ? errstr : "<NULL>"));
dbus_conn = sbus_get_connection(conn);
if (!dbus_conn) {
DEBUG(SSSDBG_CRIT_FAILURE, ("D-BUS not connected\n"));
return;
}
dbus_connection_send(dbus_conn, reply, NULL);
dbus_message_unref(reply);
}
static void be_sudo_handler_callback(struct be_req *req,
int dp_err,
int dp_ret,
const char *errstr)
{
DBusMessage *reply = NULL;
reply = (DBusMessage*)(req->pvt);
be_sudo_handler_reply(req->becli->conn, reply, dp_err, dp_ret, errstr);
talloc_free(req);
}
static int be_sudo_handler(DBusMessage *message, struct sbus_connection *conn)
{
DBusError dbus_error;
DBusMessage *reply = NULL;
DBusMessageIter iter;
dbus_bool_t iter_next = FALSE;
struct be_client *be_cli = NULL;
struct be_req *be_req = NULL;
struct be_sudo_req *sudo_req = NULL;
void *user_data = NULL;
int ret = 0;
uint32_t type;
uint32_t rules_num = 0;
const char *rule = NULL;
const char *err_msg = NULL;
int i;
DEBUG(SSSDBG_TRACE_FUNC, ("Entering be_sudo_handler()\n"));
user_data = sbus_conn_get_private_data(conn);
if (user_data == NULL) {
return EINVAL;
}
be_cli = talloc_get_type(user_data, struct be_client);
if (be_cli == NULL) {
return EINVAL;
}
reply = dbus_message_new_method_return(message);
if (!reply) {
DEBUG(SSSDBG_CRIT_FAILURE,
("dbus_message_new_method_return failed, cannot send reply.\n"));
return ENOMEM;
}
/* create be request */
be_req = be_req_create(be_cli, be_cli, be_cli->bectx,
be_sudo_handler_callback, reply);
if (be_req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed.\n"));
dbus_message_unref(reply);
return ENOMEM;
}
dbus_error_init(&dbus_error);
dbus_message_iter_init(message, &iter);
/* get type of the request */
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed, to parse the message!\n"));
ret = EIO;
err_msg = "Invalid D-Bus message format";
goto fail;
}
dbus_message_iter_get_basic(&iter, &type);
dbus_message_iter_next(&iter); /* step behind the request type */
/* If we are offline and fast reply was requested
* return offline immediately
*/
if ((type & BE_REQ_FAST) && be_cli->bectx->offstat.offline) {
be_sudo_handler_reply(conn, reply, DP_ERR_OFFLINE, EAGAIN,
"Fast reply - offline");
reply = NULL;
/* 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 */
sudo_req = talloc_zero(be_req, struct be_sudo_req);
if (sudo_req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed.\n"));
goto fail;
}
sudo_req->type = (~BE_REQ_FAST) & type;
/* get additional arguments according to the request type */
switch (sudo_req->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 */
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed, to parse the message!\n"));
ret = EIO;
err_msg = "Invalid D-Bus message format";
goto fail;
}
dbus_message_iter_get_basic(&iter, &rules_num);
sudo_req->rules = talloc_array(sudo_req, char*, rules_num + 1);
if (sudo_req->rules == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_array failed.\n"));
ret = ENOMEM;
goto fail;
}
/* read the rules */
for (i = 0; i < rules_num; i++) {
iter_next = dbus_message_iter_next(&iter);
if (iter_next == FALSE) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed, to parse the message!\n"));
ret = EIO;
err_msg = "Invalid D-Bus message format";
goto fail;
}
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed, to parse the message!\n"));
ret = EIO;
err_msg = "Invalid D-Bus message format";
goto fail;
}
dbus_message_iter_get_basic(&iter, &rule);
sudo_req->rules[i] = talloc_strdup(sudo_req->rules, rule);
if (sudo_req->rules[i] == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup failed.\n"));
ret = ENOMEM;
goto fail;
}
}
sudo_req->rules[rules_num] = NULL;
break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid request type %d\n", sudo_req->type));
ret = EINVAL;
err_msg = "Invalid DP request type";
goto fail;
}
be_req->req_data = sudo_req;
/* return an error if corresponding backend target is not configured */
if (!be_cli->bectx->bet_info[BET_SUDO].bet_ops) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Undefined backend target.\n"));
ret = ENODEV;
goto fail;
}
ret = be_file_request(be_cli->bectx->bet_info[BET_SUDO].pvt_bet_data,
be_req,
be_cli->bectx->bet_info[BET_SUDO].bet_ops->handler);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, ("be_file_request failed.\n"));
err_msg = "Cannot file back end request";
goto fail;
}
return EOK;
fail:
/* send reply back immediately */
be_sudo_handler_callback(be_req, DP_ERR_FATAL, ret,
err_msg ? err_msg : strerror(ret));
return EOK;
}
static void be_autofs_handler_callback(struct be_req *req,
int dp_err_type,
int errnum,
const char *errstr);
static int be_autofs_handler(DBusMessage *message, struct sbus_connection *conn)
{
DBusError dbus_error;
DBusMessage *reply = NULL;
dbus_bool_t dbret;
struct be_client *be_cli = NULL;
struct be_req *be_req = NULL;
struct be_autofs_req *be_autofs_req = NULL;
void *user_data = NULL;
int ret = 0;
uint32_t type;
char *filter;
char *filter_val;
dbus_uint16_t err_maj;
dbus_uint32_t err_min;
const char *err_msg;
DEBUG(SSSDBG_TRACE_FUNC, ("Entering be_autofs_handler()\n"));
user_data = sbus_conn_get_private_data(conn);
if (user_data == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get SBUS private data\n"));
return EINVAL;
}
be_cli = talloc_get_type(user_data, struct be_client);
if (be_cli == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get back end client context\n"));
return EINVAL;
}
dbus_error_init(&dbus_error);
ret = dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_UINT32, &type,
DBUS_TYPE_STRING, &filter,
DBUS_TYPE_INVALID);
if (!ret) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed, to parse message!\n"));
if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
return EIO;
}
reply = dbus_message_new_method_return(message);
if (!reply) {
DEBUG(SSSDBG_CRIT_FAILURE,
("dbus_message_new_method_return failed, cannot send reply.\n"));
return ENOMEM;
}
/* If we are offline and fast reply was requested
* return offline immediately
*/
if ((type & BE_REQ_FAST) && be_cli->bectx->offstat.offline) {
/* Send back an immediate reply */
err_maj = DP_ERR_OFFLINE;
err_min = EAGAIN;
err_msg = "Fast reply - offline";
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) return EIO;
DEBUG(SSSDBG_TRACE_LIBS, ("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
reply = NULL;
/* This reply will be queued and sent
* when we reenter the mainloop.
*
* Continue processing in case we are
* going back online.
*/
}
if (filter) {
if (strncmp(filter, "mapname=", 8) == 0) {
filter_val = &filter[8];
} else {
err_maj = DP_ERR_FATAL;
err_min = EINVAL;
err_msg = "Invalid Filter";
goto done;
}
} else {
err_maj = DP_ERR_FATAL;
err_min = EINVAL;
err_msg = "Missing Filter Parameter";
goto done;
}
/* create be request */
be_req = be_req_create(be_cli, be_cli, be_cli->bectx,
be_autofs_handler_callback, reply);
if (be_req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed.\n"));
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto done;
}
/* set autofs request data */
be_autofs_req = talloc_zero(be_req, struct be_autofs_req);
if (be_autofs_req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed.\n"));
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto done;
}
be_autofs_req->mapname = talloc_strdup(be_autofs_req, filter_val);
if (be_autofs_req->mapname == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup failed.\n"));
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
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
*/
if (strcmp(be_autofs_req->mapname, "auto.master") == 0) {
be_autofs_req->invalidate = true;
}
be_req->req_data = be_autofs_req;
if (!be_cli->bectx->bet_info[BET_AUTOFS].bet_ops) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Undefined backend target.\n"));
err_maj = DP_ERR_FATAL;
err_min = ENODEV;
err_msg = "Autofs back end target is not configured";
goto done;
}
ret = be_file_request(be_cli->bectx->bet_info[BET_AUTOFS].pvt_bet_data,
be_req,
be_cli->bectx->bet_info[BET_AUTOFS].bet_ops->handler);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, ("be_file_request failed.\n"));
err_maj = DP_ERR_FATAL;
err_min = ENODEV;
err_msg = "Cannot file back end request";
goto done;
}
return EOK;
done:
if (be_req) {
talloc_free(be_req);
}
if (reply) {
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) return EIO;
DEBUG(SSSDBG_TRACE_LIBS, ("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
/* send reply back */
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
}
return EOK;
}
static void be_autofs_handler_callback(struct be_req *req,
int dp_err_type,
int errnum,
const char *errstr)
{
DBusMessage *reply;
DBusConnection *dbus_conn;
dbus_bool_t dbret;
dbus_uint16_t err_maj = 0;
dbus_uint32_t err_min = 0;
const char *err_msg = NULL;
reply = (DBusMessage *)req->pvt;
if (reply) {
/* Return a reply if one was requested
* There may not be one if this request began
* while we were offline
*/
err_maj = dp_err_type;
err_min = errnum;
if (errstr) {
err_msg = errstr;
} else {
err_msg = dp_pam_err_to_string(req, dp_err_type, errnum);
}
if (!err_msg) {
DEBUG(SSSDBG_CRIT_FAILURE,
("Failed to set err_msg, Out of memory?\n"));
err_msg = "OOM";
}
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to generate dbus reply\n"));
return;
}
dbus_conn = sbus_get_connection(req->becli->conn);
if (!dbus_conn) {
DEBUG(SSSDBG_CRIT_FAILURE, ("D-BUS not connected\n"));
return;
}
dbus_connection_send(dbus_conn, reply, NULL);
dbus_message_unref(reply);
DEBUG(SSSDBG_TRACE_LIBS,
("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
}
/* finally free the request */
talloc_free(req);
}
static int be_host_handler(DBusMessage *message, struct sbus_connection *conn)
{
struct be_host_req *req;
struct be_req *be_req;
struct be_client *becli;
DBusMessage *reply;
DBusError dbus_error;
dbus_bool_t dbret;
void *user_data;
uint32_t flags;
char *filter;
int ret;
dbus_uint16_t err_maj;
dbus_uint32_t err_min;
const char *err_msg;
be_req = NULL;
user_data = sbus_conn_get_private_data(conn);
if (!user_data) return EINVAL;
becli = talloc_get_type(user_data, struct be_client);
if (!becli) return EINVAL;
dbus_error_init(&dbus_error);
ret = dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_UINT32, &flags,
DBUS_TYPE_STRING, &filter,
DBUS_TYPE_INVALID);
if (!ret) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed, to parse message!\n"));
if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
return EIO;
}
DEBUG(SSSDBG_TRACE_LIBS,
("Got request for [%u][%s]\n", flags, filter));
reply = dbus_message_new_method_return(message);
if (!reply) return ENOMEM;
/* If we are offline and fast reply was requested
* return offline immediately
*/
if ((flags & BE_REQ_FAST) && becli->bectx->offstat.offline) {
/* Send back an immediate reply */
err_maj = DP_ERR_OFFLINE;
err_min = EAGAIN;
err_msg = "Fast reply - offline";
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) return EIO;
DEBUG(SSSDBG_TRACE_LIBS,
("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
reply = NULL;
/* This reply will be queued and sent
* when we reenter the mainloop.
*
* Continue processing in case we are
* going back online.
*/
}
be_req = be_req_create(becli, becli, becli->bectx,
acctinfo_callback, reply);
if (!be_req) {
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto done;
}
req = talloc(be_req, struct be_host_req);
if (!req) {
err_maj = DP_ERR_FATAL;
err_min = ENOMEM;
err_msg = "Out of memory";
goto done;
}
req->type = BE_REQ_HOST | (flags & BE_REQ_FAST);
be_req->req_data = req;
if (filter) {
ret = strncmp(filter, "name=", 5);
if (ret == 0) {
req->filter_type = BE_FILTER_NAME;
ret = split_name_extended(req, &filter[5],
&req->name,
&req->alias);
}
if (ret) {
err_maj = DP_ERR_FATAL;
err_min = EINVAL;
err_msg = "Invalid Filter";
goto done;
}
} else {
err_maj = DP_ERR_FATAL;
err_min = EINVAL;
err_msg = "Missing Filter Parameter";
goto done;
}
/* process request */
if (!becli->bectx->bet_info[BET_HOSTID].bet_ops) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Undefined backend target.\n"));
err_maj = DP_ERR_FATAL;
err_min = ENODEV;
err_msg = "HostID back end target is not configured";
goto done;
}
ret = be_file_request(becli->bectx->bet_info[BET_HOSTID].pvt_bet_data,
be_req,
becli->bectx->bet_info[BET_HOSTID].bet_ops->handler);
if (ret != EOK) {
err_maj = DP_ERR_FATAL;
err_min = ret;
err_msg = "Failed to file request";
goto done;
}
return EOK;
done:
if (be_req) {
talloc_free(be_req);
}
if (reply) {
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &err_maj,
DBUS_TYPE_UINT32, &err_min,
DBUS_TYPE_STRING, &err_msg,
DBUS_TYPE_INVALID);
if (!dbret) return EIO;
DEBUG(SSSDBG_TRACE_LIBS,
("Request processed. Returned %d,%d,%s\n",
err_maj, err_min, err_msg));
/* send reply back */
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
}
return EOK;
}
static int be_client_destructor(void *ctx)
{
struct be_client *becli = talloc_get_type(ctx, struct be_client);
if (becli->bectx) {
if (becli->bectx->nss_cli == becli) {
DEBUG(SSSDBG_TRACE_FUNC, ("Removed NSS client\n"));
becli->bectx->nss_cli = NULL;
} else if (becli->bectx->pam_cli == becli) {
DEBUG(SSSDBG_TRACE_FUNC, ("Removed PAM client\n"));
becli->bectx->pam_cli = NULL;
} else if (becli->bectx->sudo_cli == becli) {
DEBUG(SSSDBG_TRACE_FUNC, ("Removed SUDO client\n"));
becli->bectx->sudo_cli = NULL;
} else if (becli->bectx->autofs_cli == becli) {
DEBUG(SSSDBG_TRACE_FUNC, ("Removed autofs client\n"));
becli->bectx->autofs_cli = NULL;
} else if (becli->bectx->ssh_cli == becli) {
DEBUG(SSSDBG_TRACE_FUNC, ("Removed SSH client\n"));
becli->bectx->ssh_cli = NULL;
} else if (becli->bectx->pac_cli == becli) {
DEBUG(SSSDBG_TRACE_FUNC, ("Removed PAC client\n"));
becli->bectx->pac_cli = NULL;
} else {
DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown client removed ...\n"));
}
}
return 0;
}
static int client_registration(DBusMessage *message,
struct sbus_connection *conn)
{
dbus_uint16_t version = DATA_PROVIDER_VERSION;
struct be_client *becli;
DBusMessage *reply;
DBusError dbus_error;
dbus_uint16_t cli_ver;
char *cli_name;
dbus_bool_t dbret;
void *data;
data = sbus_conn_get_private_data(conn);
becli = talloc_get_type(data, struct be_client);
if (!becli) {
DEBUG(0, ("Connection holds no valid init data\n"));
return EINVAL;
}
/* First thing, cancel the timeout */
DEBUG(4, ("Cancel DP ID timeout [%p]\n", becli->timeout));
talloc_zfree(becli->timeout);
dbus_error_init(&dbus_error);
dbret = dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_UINT16, &cli_ver,
DBUS_TYPE_STRING, &cli_name,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(1, ("Failed to parse message, killing connection\n"));
if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
sbus_disconnect(conn);
/* FIXME: should we just talloc_zfree(conn) ? */
return EIO;
}
if (strcasecmp(cli_name, "NSS") == 0) {
becli->bectx->nss_cli = becli;
} else if (strcasecmp(cli_name, "PAM") == 0) {
becli->bectx->pam_cli = becli;
} else if (strcasecmp(cli_name, "SUDO") == 0) {
becli->bectx->sudo_cli = becli;
} else if (strcasecmp(cli_name, "autofs") == 0) {
becli->bectx->autofs_cli = becli;
} else if (strcasecmp(cli_name, "SSH") == 0) {
becli->bectx->ssh_cli = becli;
} else if (strcasecmp(cli_name, "PAC") == 0) {
becli->bectx->pac_cli = becli;
} else {
DEBUG(1, ("Unknown client! [%s]\n", cli_name));
}
talloc_set_destructor((TALLOC_CTX *)becli, be_client_destructor);
DEBUG(4, ("Added Frontend client [%s]\n", cli_name));
/* reply that all is ok */
reply = dbus_message_new_method_return(message);
if (!reply) {
DEBUG(0, ("Dbus Out of memory!\n"));
return ENOMEM;
}
dbret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &version,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(0, ("Failed to build dbus reply\n"));
dbus_message_unref(reply);
sbus_disconnect(conn);
return EIO;
}
/* send reply back */
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
becli->initialized = true;
return EOK;
}
static errno_t be_file_check_online_request(struct be_req *req)
{
int ret;
req->be_ctx->offstat.went_offline = time(NULL);
reset_fo(req->be_ctx);
ret = be_file_request(req->be_ctx, req,
req->be_ctx->bet_info[BET_ID].bet_ops->check_online);
if (ret != EOK) {
DEBUG(1, ("be_file_request failed.\n"));
}
return ret;
}
static void check_online_callback(struct be_req *req, int dp_err_type,
int errnum, const char *errstr)
{
int ret;
DEBUG(4, ("Backend returned: (%d, %d, %s) [%s]\n",
dp_err_type, errnum, errstr?errstr:"<NULL>",
dp_pam_err_to_string(req, dp_err_type, errnum)));
req->be_ctx->check_online_ref_count--;
if (dp_err_type != DP_ERR_OK && req->be_ctx->check_online_ref_count > 0) {
ret = be_file_check_online_request(req);
if (ret != EOK) {
DEBUG(1, ("be_file_check_online_request failed.\n"));
goto done;
}
return;
}
done:
req->be_ctx->check_online_ref_count = 0;
if (dp_err_type != DP_ERR_OFFLINE) {
if (dp_err_type != DP_ERR_OK) {
reset_fo(req->be_ctx);
}
be_reset_offline(req->be_ctx);
}
talloc_free(req);
return;
}
static void check_if_online(struct be_ctx *ctx)
{
int ret;
struct be_req *be_req = NULL;
be_run_unconditional_online_cb(ctx);
if (ctx->offstat.offline == false) {
DEBUG(8, ("Backend is already online, nothing to do.\n"));
return;
}
/* Make sure nobody tries to go online while we are checking */
ctx->offstat.went_offline = time(NULL);
DEBUG(8, ("Trying to go back online!\n"));
ctx->check_online_ref_count++;
if (ctx->check_online_ref_count != 1) {
DEBUG(8, ("There is an online check already running.\n"));
return;
}
if (ctx->bet_info[BET_ID].bet_ops->check_online == NULL) {
DEBUG(8, ("ID providers does not provide a check_online method.\n"));
goto failed;
}
be_req = be_req_create(ctx, NULL, ctx,
check_online_callback, NULL);
if (be_req == NULL) {
DEBUG(1, ("talloc_zero failed.\n"));
goto failed;
}
ret = be_file_check_online_request(be_req);
if (ret != EOK) {
DEBUG(1, ("be_file_check_online_request failed.\n"));
goto failed;
}
return;
failed:
ctx->check_online_ref_count--;
DEBUG(1, ("Failed to run a check_online test.\n"));
talloc_free(be_req);
if (ctx->check_online_ref_count == 0) {
reset_fo(ctx);
be_reset_offline(ctx);
}
return;
}
static void init_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval t, void *ptr)
{
struct be_client *becli;
DEBUG(2, ("Client timed out before Identification [%p]!\n", te));
becli = talloc_get_type(ptr, struct be_client);
sbus_disconnect(becli->conn);
talloc_zfree(becli);
}
static int be_client_init(struct sbus_connection *conn, void *data)
{
struct be_ctx *bectx;
struct be_client *becli;
struct timeval tv;
bectx = talloc_get_type(data, struct be_ctx);
/* hang off this memory to the connection so that when the connection
* is freed we can potentially call a destructor */
becli = talloc(conn, struct be_client);
if (!becli) {
DEBUG(0,("Out of memory?!\n"));
talloc_zfree(conn);
return ENOMEM;
}
becli->bectx = bectx;
becli->conn = conn;
becli->initialized = false;
/* 5 seconds should be plenty */
tv = tevent_timeval_current_ofs(5, 0);
becli->timeout = tevent_add_timer(bectx->ev, becli,
tv, init_timeout, becli);
if (!becli->timeout) {
DEBUG(0,("Out of memory?!\n"));
talloc_zfree(conn);
return ENOMEM;
}
DEBUG(4, ("Set-up Backend ID timeout [%p]\n", becli->timeout));
/* Attach the client context to the connection context, so that it is
* always available when we need to manage the connection. */
sbus_conn_set_private_data(conn, becli);
return EOK;
}
/* be_srv_init
* set up per-domain sbus channel */
static int be_srv_init(struct be_ctx *ctx)
{
char *sbus_address;
int ret;
/* Set up SBUS connection to the monitor */
ret = dp_get_sbus_address(ctx, &sbus_address, ctx->domain->name);
if (ret != EOK) {
DEBUG(0, ("Could not get sbus backend address.\n"));
return ret;
}
ret = sbus_new_server(ctx, ctx->ev, sbus_address,
&be_interface, true, &ctx->sbus_srv,
be_client_init, ctx);
if (ret != EOK) {
DEBUG(0, ("Could not set up sbus server.\n"));
return ret;
}
return EOK;
}
static void be_target_access_permit(struct be_req *be_req)
{
struct pam_data *pd =
talloc_get_type(be_req_get_data(be_req), struct pam_data);
DEBUG(9, ("be_target_access_permit called, returning PAM_SUCCESS.\n"));
pd->pam_status = PAM_SUCCESS;
be_req_terminate(be_req, DP_ERR_OK, PAM_SUCCESS, NULL);
}
static struct bet_ops be_target_access_permit_ops = {
.check_online = NULL,
.handler = be_target_access_permit,
.finalize = NULL
};
static void be_target_access_deny(struct be_req *be_req)
{
struct pam_data *pd =
talloc_get_type(be_req_get_data(be_req), struct pam_data);
DEBUG(9, ("be_target_access_deny called, returning PAM_PERM_DENIED.\n"));
pd->pam_status = PAM_PERM_DENIED;
be_req_terminate(be_req, DP_ERR_OK, PAM_PERM_DENIED, NULL);
}
static struct bet_ops be_target_access_deny_ops = {
.check_online = NULL,
.handler = be_target_access_deny,
.finalize = NULL
};
static int load_backend_module(struct be_ctx *ctx,
enum bet_type bet_type,
struct bet_info *bet_info,
const char *default_mod_name)
{
TALLOC_CTX *tmp_ctx;
int ret = EINVAL;
bool already_loaded = false;
int lb=0;
char *mod_name = NULL;
char *path = NULL;
void *handle;
char *mod_init_fn_name = NULL;
bet_init_fn_t mod_init_fn = NULL;
(*bet_info).bet_type = bet_type;
(*bet_info).mod_name = NULL;
(*bet_info).bet_ops = NULL;
(*bet_info).pvt_bet_data = NULL;
if (bet_type <= BET_NULL || bet_type >= BET_MAX ||
bet_type != bet_data[bet_type].bet_type) {
DEBUG(2, ("invalid bet_type or bet_data corrupted.\n"));
return EINVAL;
}
tmp_ctx = talloc_new(ctx);
if (!tmp_ctx) {
DEBUG(7, ("talloc_new failed.\n"));
return ENOMEM;
}
ret = confdb_get_string(ctx->cdb, tmp_ctx, ctx->conf_path,
bet_data[bet_type].option_name, NULL,
&mod_name);
if (ret != EOK) {
ret = EFAULT;
goto done;
}
if (!mod_name) {
if (default_mod_name != NULL) {
DEBUG(5, ("no module name found in confdb, using [%s].\n",
default_mod_name));
mod_name = talloc_strdup(ctx, default_mod_name);
} else {
ret = ENOENT;
goto done;
}
}
if (strcasecmp(mod_name, NO_PROVIDER) == 0) {
ret = ENOENT;
goto done;
}
if (bet_type == BET_ACCESS) {
if (strcmp(mod_name, ACCESS_PERMIT) == 0) {
(*bet_info).bet_ops = &be_target_access_permit_ops;
(*bet_info).pvt_bet_data = NULL;
(*bet_info).mod_name = talloc_strdup(ctx, ACCESS_PERMIT);
ret = EOK;
goto done;
}
if (strcmp(mod_name, ACCESS_DENY) == 0) {
(*bet_info).bet_ops = &be_target_access_deny_ops;
(*bet_info).pvt_bet_data = NULL;
(*bet_info).mod_name = talloc_strdup(ctx, ACCESS_DENY);
ret = EOK;
goto done;
}
}
mod_init_fn_name = talloc_asprintf(tmp_ctx,
bet_data[bet_type].mod_init_fn_name_fmt,
mod_name);
if (mod_init_fn_name == NULL) {
DEBUG(7, ("talloc_asprintf failed\n"));
ret = ENOMEM;
goto done;
}
lb = 0;
while(ctx->loaded_be[lb].be_name != NULL) {
if (strncmp(ctx->loaded_be[lb].be_name, mod_name,
strlen(mod_name)) == 0) {
DEBUG(7, ("Backend [%s] already loaded.\n", mod_name));
already_loaded = true;
break;
}
++lb;
if (lb >= BET_MAX) {
DEBUG(2, ("Backend context corrupted.\n"));
ret = EINVAL;
goto done;
}
}
if (!already_loaded) {
path = talloc_asprintf(tmp_ctx, "%s/libsss_%s.so",
DATA_PROVIDER_PLUGINS_PATH, mod_name);
if (!path) {
ret = ENOMEM;
goto done;
}
DEBUG(7, ("Loading backend [%s] with path [%s].\n", mod_name, path));
handle = dlopen(path, RTLD_NOW);
if (!handle) {
DEBUG(0, ("Unable to load %s module with path (%s), error: %s\n",
mod_name, path, dlerror()));
ret = ELIBACC;
goto done;
}
ctx->loaded_be[lb].be_name = talloc_strdup(ctx, mod_name);
ctx->loaded_be[lb].handle = handle;
}
mod_init_fn = (bet_init_fn_t)dlsym(ctx->loaded_be[lb].handle,
mod_init_fn_name);
if (mod_init_fn == NULL) {
if (default_mod_name != NULL &&
strcmp(default_mod_name, mod_name) == 0 ) {
/* 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. */
ret = ENOENT;
} else {
DEBUG(0, ("Unable to load init fn %s from module %s, error: %s\n",
mod_init_fn_name, mod_name, dlerror()));
ret = ELIBBAD;
}
goto done;
}
ret = mod_init_fn(ctx, &(*bet_info).bet_ops, &(*bet_info).pvt_bet_data);
if (ret != EOK) {
DEBUG(0, ("Error (%d) in module (%s) initialization (%s)!\n",
ret, mod_name, mod_init_fn_name));
goto done;
}
(*bet_info).mod_name = talloc_strdup(ctx, mod_name);
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
static void signal_be_offline(struct tevent_context *ev,
struct tevent_signal *se,
int signum,
int count,
void *siginfo,
void *private_data)
{
struct be_ctx *ctx = talloc_get_type(private_data, struct be_ctx);
be_mark_offline(ctx);
}
int be_process_init_sudo(struct be_ctx *be_ctx)
{
TALLOC_CTX *tmp_ctx = NULL;
char **services = NULL;
char *provider = NULL;
bool responder_enabled = false;
int i;
int ret;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
return ENOMEM;
}
ret = confdb_get_string_as_list(be_ctx->cdb, tmp_ctx,
CONFDB_MONITOR_CONF_ENTRY,
CONFDB_MONITOR_ACTIVE_SERVICES, &services);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE, ("Unable to read from confdb [%d]: %s\n",
ret, strerror(ret)));
goto done;
}
for (i = 0; services[i] != NULL; i++) {
if (strcmp(services[i], "sudo") == 0) {
responder_enabled = true;
break;
}
}
ret = confdb_get_string(be_ctx->cdb, tmp_ctx, be_ctx->conf_path,
CONFDB_DOMAIN_SUDO_PROVIDER, NULL, &provider);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE, ("Unable to read from confdb [%d]: %s\n",
ret, strerror(ret)));
goto done;
}
if (!responder_enabled && provider == NULL) {
/* provider is not set explicitly */
DEBUG(SSSDBG_TRACE_FUNC,
("SUDO is not listed in services, disabling SUDO module.\n"));
ret = ENOENT;
goto done;
}
if (!responder_enabled && provider != NULL
&& strcmp(provider, NO_PROVIDER) != 0) {
/* provider is set but responder is disabled */
DEBUG(SSSDBG_MINOR_FAILURE, ("SUDO provider is set, but it is not "
"listed in active services. SUDO support will not work!\n"));
}
ret = load_backend_module(be_ctx, BET_SUDO, &be_ctx->bet_info[BET_SUDO],
be_ctx->bet_info[BET_ID].mod_name);
done:
talloc_free(tmp_ctx);
return ret;
}
int be_process_init(TALLOC_CTX *mem_ctx,
const char *be_domain,
struct tevent_context *ev,
struct confdb_ctx *cdb)
{
struct be_ctx *ctx;
struct tevent_signal *tes;
int ret;
ctx = talloc_zero(mem_ctx, struct be_ctx);
if (!ctx) {
DEBUG(0, ("fatal error initializing be_ctx\n"));
return ENOMEM;
}
ctx->ev = ev;
ctx->cdb = cdb;
ctx->identity = talloc_asprintf(ctx, "%%BE_%s", be_domain);
ctx->conf_path = talloc_asprintf(ctx, CONFDB_DOMAIN_PATH_TMPL, be_domain);
if (!ctx->identity || !ctx->conf_path) {
DEBUG(0, ("Out of memory!?\n"));
ret = ENOMEM;
goto fail;
}
ret = be_init_failover(ctx);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE,
("fatal error initializing failover context\n"));
goto fail;
}
ret = sssd_domain_init(ctx, cdb, be_domain, DB_PATH, &ctx->domain);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error opening cache database\n"));
goto fail;
}
ret = sss_monitor_init(ctx, ctx->ev, &monitor_be_interface,
ctx->identity, DATA_PROVIDER_VERSION,
ctx, &ctx->mon_conn);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE,
("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 */
ret = sss_names_init(ctx->domain, cdb,
ctx->domain->name, &ctx->domain->names);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE,
("fatal error setting fully qualified name format for %s\n",
ctx->domain->name));
goto fail;
}
ret = be_srv_init(ctx);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error setting up server bus\n"));
goto fail;
}
/* Initialize be_refresh periodic task. */
ctx->refresh_ctx = be_refresh_ctx_init(ctx);
if (ctx->refresh_ctx == NULL) {
DEBUG(SSSDBG_FATAL_FAILURE, ("Unable to initialize refresh_ctx\n"));
ret = ENOMEM;
goto fail;
}
if (ctx->domain->refresh_expired_interval > 0) {
ret = be_ptask_create(ctx, ctx, ctx->domain->refresh_expired_interval,
30, 5, ctx->domain->refresh_expired_interval,
BE_PTASK_OFFLINE_SKIP,
be_refresh_send, be_refresh_recv,
ctx->refresh_ctx, "Refresh Records", NULL);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE,
("Unable to initialize refresh periodic task\n"));
goto fail;
}
}
ret = load_backend_module(ctx, BET_ID,
&ctx->bet_info[BET_ID], NULL);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE,
("fatal error initializing data providers\n"));
goto fail;
}
DEBUG(SSSDBG_TRACE_INTERNAL,
("ID backend target successfully loaded from provider [%s].\n",
ctx->bet_info[BET_ID].mod_name));
ret = load_backend_module(ctx, BET_AUTH,
&ctx->bet_info[BET_AUTH],
ctx->bet_info[BET_ID].mod_name);
if (ret != EOK) {
if (ret != ENOENT) {
DEBUG(SSSDBG_FATAL_FAILURE,
("fatal error initializing data providers\n"));
goto fail;
}
DEBUG(SSSDBG_MINOR_FAILURE,
("No authentication module provided for [%s] !!\n",
be_domain));
} else {
DEBUG(SSSDBG_TRACE_INTERNAL,
("AUTH backend target successfully loaded "
"from provider [%s].\n", ctx->bet_info[BET_AUTH].mod_name));
}
ret = load_backend_module(ctx, BET_ACCESS, &ctx->bet_info[BET_ACCESS],
ACCESS_PERMIT);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE,
("Failed to setup ACCESS backend.\n"));
goto fail;
}
DEBUG(SSSDBG_TRACE_INTERNAL,
("ACCESS backend target successfully loaded "
"from provider [%s].\n", ctx->bet_info[BET_ACCESS].mod_name));
ret = load_backend_module(ctx, BET_CHPASS,
&ctx->bet_info[BET_CHPASS],
ctx->bet_info[BET_AUTH].mod_name);
if (ret != EOK) {
if (ret != ENOENT) {
DEBUG(SSSDBG_FATAL_FAILURE,
("fatal error initializing data providers\n"));
goto fail;
}
DEBUG(SSSDBG_MINOR_FAILURE,
("No change password module provided for [%s] !!\n",
be_domain));
} else {
DEBUG(SSSDBG_TRACE_INTERNAL,
("CHPASS backend target successfully loaded "
"from provider [%s].\n", ctx->bet_info[BET_CHPASS].mod_name));
}
ret = be_process_init_sudo(ctx);
if (ret != EOK) {
if (ret != ENOENT) {
DEBUG(SSSDBG_FATAL_FAILURE,
("fatal error initializing data providers\n"));
goto fail;
}
DEBUG(SSSDBG_MINOR_FAILURE,
("No SUDO module provided for [%s] !!\n", be_domain));
} else {
DEBUG(SSSDBG_TRACE_INTERNAL,
("SUDO backend target successfully loaded "
"from provider [%s].\n", ctx->bet_info[BET_SUDO].mod_name));
}
ret = load_backend_module(ctx, BET_AUTOFS,
&ctx->bet_info[BET_AUTOFS],
ctx->bet_info[BET_ID].mod_name);
if (ret != EOK) {
if (ret != ENOENT) {
DEBUG(SSSDBG_FATAL_FAILURE,
("fatal error initializing data providers\n"));
goto fail;
}
DEBUG(SSSDBG_MINOR_FAILURE,
("No autofs module provided for [%s] !!\n", be_domain));
} else {
DEBUG(SSSDBG_TRACE_INTERNAL,
("autofs backend target successfully loaded "
"from provider [%s].\n", ctx->bet_info[BET_AUTOFS].mod_name));
}
ret = load_backend_module(ctx, BET_SELINUX,
&ctx->bet_info[BET_SELINUX],
ctx->bet_info[BET_ID].mod_name);
if (ret != EOK) {
if (ret != ENOENT) {
DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error initializing data providers\n"));
goto fail;
}
DEBUG(SSSDBG_CRIT_FAILURE, ("No selinux module provided for [%s] !!\n",
be_domain));
} else {
DEBUG(SSSDBG_TRACE_ALL, ("selinux backend target successfully loaded "
"from provider [%s].\n", ctx->bet_info[BET_SELINUX].mod_name));
}
ret = load_backend_module(ctx, BET_HOSTID,
&ctx->bet_info[BET_HOSTID],
ctx->bet_info[BET_ID].mod_name);
if (ret != EOK) {
if (ret != ENOENT) {
DEBUG(SSSDBG_FATAL_FAILURE,
("fatal error initializing data providers\n"));
goto fail;
}
DEBUG(SSSDBG_CRIT_FAILURE,
("No host info module provided for [%s] !!\n", be_domain));
} else {
DEBUG(SSSDBG_TRACE_ALL,
("HOST backend target successfully loaded from provider [%s].\n",
ctx->bet_info[BET_HOSTID].mod_name));
}
ret = load_backend_module(ctx, BET_SUBDOMAINS,
&ctx->bet_info[BET_SUBDOMAINS],
ctx->bet_info[BET_ID].mod_name);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Subdomains are not supported for [%s] !!\n", be_domain));
} else {
DEBUG(SSSDBG_TRACE_ALL, ("Get-Subdomains backend target successfully loaded "
"from provider [%s].\n",
ctx->bet_info[BET_SUBDOMAINS].mod_name));
}
/* Handle SIGUSR1 to force offline behavior */
BlockSignals(false, SIGUSR1);
tes = tevent_add_signal(ctx->ev, ctx, SIGUSR1, 0,
signal_be_offline, ctx);
if (tes == NULL) {
ret = EIO;
goto fail;
}
return EOK;
fail:
talloc_free(ctx);
return ret;
}
#ifndef UNIT_TESTING
int main(int argc, const char *argv[])
{
int opt;
poptContext pc;
char *be_domain = NULL;
char *srv_name = NULL;
struct main_context *main_ctx;
char *confdb_path;
int ret;
struct poptOption long_options[] = {
POPT_AUTOHELP
SSSD_MAIN_OPTS
{"domain", 0, POPT_ARG_STRING, &be_domain, 0,
_("Domain of the information provider (mandatory)"), NULL },
POPT_TABLEEND
};
/* Set debug level to invalid value so we can deside if -d 0 was used. */
debug_level = SSSDBG_INVALID;
pc = poptGetContext(argv[0], argc, argv, long_options, 0);
while((opt = poptGetNextOpt(pc)) != -1) {
switch(opt) {
default:
fprintf(stderr, "\nInvalid option %s: %s\n\n",
poptBadOption(pc, 0), poptStrerror(opt));
poptPrintUsage(pc, stderr, 0);
return 1;
}
}
if (be_domain == NULL) {
fprintf(stderr, "\nMissing option, --domain is a mandatory option.\n\n");
poptPrintUsage(pc, stderr, 0);
return 1;
}
poptFreeContext(pc);
DEBUG_INIT(debug_level);
/* set up things like debug , signals, daemonization, etc... */
debug_log_file = talloc_asprintf(NULL, "sssd_%s", be_domain);
if (!debug_log_file) return 2;
srv_name = talloc_asprintf(NULL, "sssd[be[%s]]", be_domain);
if (!srv_name) return 2;
confdb_path = talloc_asprintf(NULL, CONFDB_DOMAIN_PATH_TMPL, be_domain);
if (!confdb_path) return 2;
ret = server_setup(srv_name, 0, confdb_path, &main_ctx);
if (ret != EOK) {
DEBUG(0, ("Could not set up mainloop [%d]\n", ret));
return 2;
}
ret = setenv(SSS_DOM_ENV, be_domain, 1);
if (ret != 0) {
DEBUG(SSSDBG_MINOR_FAILURE, ("Setting "SSS_DOM_ENV" failed, journald "
"logging mightnot work as expected\n"));
}
ret = die_if_parent_died();
if (ret != EOK) {
/* This is not fatal, don't return */
DEBUG(2, ("Could not set up to exit when parent process does\n"));
}
ret = be_process_init(main_ctx,
be_domain,
main_ctx->event_ctx,
main_ctx->confdb_ctx);
if (ret != EOK) {
DEBUG(0, ("Could not initialize backend [%d]\n", ret));
return 3;
}
DEBUG(SSSDBG_TRACE_FUNC, ("Backend provider (%s) started!\n", be_domain));
/* loop on main */
server_loop(main_ctx);
return 0;
}
#endif
static int data_provider_res_init(DBusMessage *message,
struct sbus_connection *conn)
{
struct be_ctx *be_ctx;
be_ctx = talloc_get_type(sbus_conn_get_private_data(conn), struct be_ctx);
resolv_reread_configuration(be_ctx->be_res->resolv);
check_if_online(be_ctx);
return monitor_common_res_init(message, conn);
}
static int data_provider_go_offline(DBusMessage *message,
struct sbus_connection *conn)
{
struct be_ctx *be_ctx;
be_ctx = talloc_get_type(sbus_conn_get_private_data(conn), struct be_ctx);
be_mark_offline(be_ctx);
return monitor_common_pong(message, conn);
}
static int data_provider_reset_offline(DBusMessage *message,
struct sbus_connection *conn)
{
struct be_ctx *be_ctx;
be_ctx = talloc_get_type(sbus_conn_get_private_data(conn), struct be_ctx);
check_if_online(be_ctx);
return monitor_common_pong(message, conn);
}
static int data_provider_logrotate(DBusMessage *message,
struct sbus_connection *conn)
{
errno_t ret;
struct be_ctx *be_ctx = talloc_get_type(sbus_conn_get_private_data(conn),
struct be_ctx);
ret = monitor_common_rotate_logs(be_ctx->cdb, be_ctx->conf_path);
if (ret != EOK) return ret;
return monitor_common_pong(message, conn);
}