summaryrefslogtreecommitdiff
path: root/src/providers/proxy/proxy_child.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/proxy/proxy_child.c')
-rw-r--r--src/providers/proxy/proxy_child.c507
1 files changed, 507 insertions, 0 deletions
diff --git a/src/providers/proxy/proxy_child.c b/src/providers/proxy/proxy_child.c
new file mode 100644
index 00000000..c8f1eeb5
--- /dev/null
+++ b/src/providers/proxy/proxy_child.c
@@ -0,0 +1,507 @@
+/*
+ SSSD
+
+ Pam Proxy Child
+
+ Authors:
+
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ 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 <security/pam_appl.h>
+#include <security/pam_modules.h>
+
+#include "popt.h"
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "dbus/dbus.h"
+#include "sbus/sssd_dbus.h"
+#include "providers/proxy/proxy.h"
+
+#include "providers/dp_backend.h"
+
+static int pc_pam_handler(DBusMessage *message, struct sbus_connection *conn);
+
+struct sbus_method pc_methods[] = {
+ { DP_METHOD_PAMHANDLER, pc_pam_handler },
+ { NULL, NULL }
+};
+
+struct sbus_interface pc_interface = {
+ DP_INTERFACE,
+ DP_PATH,
+ SBUS_DEFAULT_VTABLE,
+ pc_methods,
+ NULL
+};
+
+struct pc_ctx {
+ struct tevent_context *ev;
+ struct confdb_ctx *cdb;
+ struct sss_domain_info *domain;
+ const char *identity;
+ const char *conf_path;
+ struct sbus_connection *mon_conn;
+ struct sbus_connection *conn;
+ const char *pam_target;
+ uint32_t id;
+};
+
+struct authtok_conv {
+ uint32_t authtok_size;
+ uint8_t *authtok;
+};
+
+static int proxy_internal_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response,
+ void *appdata_ptr) {
+ int i;
+ struct pam_response *reply;
+ struct authtok_conv *auth_data;
+
+ auth_data = talloc_get_type(appdata_ptr, struct authtok_conv);
+
+ if (num_msg <= 0) return PAM_CONV_ERR;
+
+ reply = (struct pam_response *) calloc(num_msg,
+ sizeof(struct pam_response));
+ if (reply == NULL) return PAM_CONV_ERR;
+
+ for (i=0; i < num_msg; i++) {
+ switch( msgm[i]->msg_style ) {
+ case PAM_PROMPT_ECHO_OFF:
+ DEBUG(4, ("Conversation message: [%s]\n", msgm[i]->msg));
+ reply[i].resp_retcode = 0;
+ reply[i].resp = calloc(auth_data->authtok_size + 1,
+ sizeof(char));
+ if (reply[i].resp == NULL) goto failed;
+ memcpy(reply[i].resp, auth_data->authtok,
+ auth_data->authtok_size);
+
+ break;
+ default:
+ DEBUG(1, ("Conversation style %d not supported.\n",
+ msgm[i]->msg_style));
+ goto failed;
+ }
+ }
+
+ *response = reply;
+ reply = NULL;
+
+ return PAM_SUCCESS;
+
+failed:
+ free(reply);
+ return PAM_CONV_ERR;
+}
+
+static errno_t call_pam_stack(const char *pam_target, struct pam_data *pd)
+{
+ int ret;
+ int pam_status;
+ pam_handle_t *pamh=NULL;
+ struct authtok_conv *auth_data;
+ struct pam_conv conv;
+
+ conv.conv=proxy_internal_conv;
+ auth_data = talloc_zero(pd, struct authtok_conv);
+ conv.appdata_ptr=auth_data;
+
+ ret = pam_start(pam_target, pd->user, &conv, &pamh);
+ if (ret == PAM_SUCCESS) {
+ DEBUG(7, ("Pam transaction started with service name [%s].\n",
+ pam_target));
+ ret = pam_set_item(pamh, PAM_TTY, pd->tty);
+ if (ret != PAM_SUCCESS) {
+ DEBUG(1, ("Setting PAM_TTY failed: %s.\n",
+ pam_strerror(pamh, ret)));
+ }
+ ret = pam_set_item(pamh, PAM_RUSER, pd->ruser);
+ if (ret != PAM_SUCCESS) {
+ DEBUG(1, ("Setting PAM_RUSER failed: %s.\n",
+ pam_strerror(pamh, ret)));
+ }
+ ret = pam_set_item(pamh, PAM_RHOST, pd->rhost);
+ if (ret != PAM_SUCCESS) {
+ DEBUG(1, ("Setting PAM_RHOST failed: %s.\n",
+ pam_strerror(pamh, ret)));
+ }
+ switch (pd->cmd) {
+ case SSS_PAM_AUTHENTICATE:
+ auth_data->authtok_size = pd->authtok_size;
+ auth_data->authtok = pd->authtok;
+ pam_status = pam_authenticate(pamh, 0);
+ break;
+ case SSS_PAM_SETCRED:
+ pam_status=pam_setcred(pamh, 0);
+ break;
+ case SSS_PAM_ACCT_MGMT:
+ pam_status=pam_acct_mgmt(pamh, 0);
+ break;
+ case SSS_PAM_OPEN_SESSION:
+ pam_status=pam_open_session(pamh, 0);
+ break;
+ case SSS_PAM_CLOSE_SESSION:
+ pam_status=pam_close_session(pamh, 0);
+ break;
+ case SSS_PAM_CHAUTHTOK:
+ if (pd->priv != 1) {
+ auth_data->authtok_size = pd->authtok_size;
+ auth_data->authtok = pd->authtok;
+ pam_status = pam_authenticate(pamh, 0);
+ if (pam_status != PAM_SUCCESS) break;
+ }
+ auth_data->authtok_size = pd->newauthtok_size;
+ auth_data->authtok = pd->newauthtok;
+ pam_status = pam_chauthtok(pamh, 0);
+ break;
+ case SSS_PAM_CHAUTHTOK_PRELIM:
+ if (pd->priv != 1) {
+ auth_data->authtok_size = pd->authtok_size;
+ auth_data->authtok = pd->authtok;
+ pam_status = pam_authenticate(pamh, 0);
+ } else {
+ pam_status = PAM_SUCCESS;
+ }
+ break;
+ default:
+ DEBUG(1, ("unknown PAM call\n"));
+ pam_status=PAM_ABORT;
+ }
+
+ DEBUG(4, ("Pam result: [%d][%s]\n", pam_status,
+ pam_strerror(pamh, pam_status)));
+
+ ret = pam_end(pamh, pam_status);
+ if (ret != PAM_SUCCESS) {
+ pamh=NULL;
+ DEBUG(1, ("Cannot terminate pam transaction.\n"));
+ }
+
+ } else {
+ DEBUG(1, ("Failed to initialize pam transaction.\n"));
+ pam_status = PAM_SYSTEM_ERR;
+ }
+
+ pd->pam_status = pam_status;
+
+ return EOK;
+}
+
+static int pc_pam_handler(DBusMessage *message, struct sbus_connection *conn)
+{
+ DBusError dbus_error;
+ DBusMessage *reply;
+ struct pc_ctx *pc_ctx;
+ errno_t ret;
+ void *user_data;
+ struct pam_data *pd = NULL;
+
+ user_data = sbus_conn_get_private_data(conn);
+ if (!user_data) {
+ ret = EINVAL;
+ goto done;
+ }
+ pc_ctx = talloc_get_type(user_data, struct pc_ctx);
+ if (!pc_ctx) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply) {
+ DEBUG(1, ("dbus_message_new_method_return failed, "
+ "cannot send reply.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dbus_error_init(&dbus_error);
+
+ ret = dp_unpack_pam_request(message, pc_ctx, &pd, &dbus_error);
+ if (!ret) {
+ DEBUG(1,("Failed, to parse message!\n"));
+ ret = EIO;
+ goto done;
+ }
+
+ pd->pam_status = PAM_SYSTEM_ERR;
+ pd->domain = talloc_strdup(pd, pc_ctx->domain->name);
+ if (pd->domain == NULL) {
+ talloc_free(pd);
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(4, ("Got request with the following data\n"));
+ DEBUG_PAM_DATA(4, pd);
+
+ ret = call_pam_stack(pc_ctx->pam_target, pd);
+ if (ret != EOK) {
+ DEBUG(1, ("call_pam_stack failed.\n"));
+ }
+
+ 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(pd);
+ dbus_message_unref(reply);
+ ret = EIO;
+ goto done;
+ }
+
+ sbus_conn_send_reply(conn, reply);
+ dbus_message_unref(reply);
+ talloc_free(pd);
+
+ /* We'll return the message and let the
+ * parent process kill us.
+ */
+ return EOK;
+
+done:
+ exit(ret);
+}
+
+int proxy_child_send_id(struct sbus_connection *conn,
+ uint16_t version,
+ uint32_t id);
+static int proxy_cli_init(struct pc_ctx *ctx)
+{
+ char *sbus_address;
+ int ret;
+
+ sbus_address = talloc_asprintf(ctx, "unix:path=%s/%s_%s",
+ PIPE_PATH, PROXY_CHILD_PIPE,
+ ctx->domain->name);
+ if (sbus_address == NULL) {
+ DEBUG(1, ("talloc_asprintf failed.\n"));
+ return ENOMEM;
+ }
+
+ ret = sbus_client_init(ctx, ctx->ev, sbus_address,
+ &pc_interface, &ctx->conn,
+ NULL, ctx);
+ if (ret != EOK) {
+ DEBUG(1, ("sbus_client_init failed.\n"));
+ return ret;
+ }
+
+ ret = proxy_child_send_id(ctx->conn, DATA_PROVIDER_VERSION, ctx->id);
+ if (ret != EOK) {
+ DEBUG(0, ("dp_common_send_id failed.\n"));
+ return ret;
+ }
+
+ return EOK;
+}
+
+int proxy_child_send_id(struct sbus_connection *conn,
+ uint16_t version,
+ uint32_t id)
+{
+ DBusMessage *msg;
+ dbus_bool_t ret;
+ int retval;
+
+ /* create the message */
+ msg = dbus_message_new_method_call(NULL,
+ DP_PATH,
+ DP_INTERFACE,
+ DP_METHOD_REGISTER);
+ if (msg == NULL) {
+ DEBUG(0, ("Out of memory?!\n"));
+ return ENOMEM;
+ }
+
+ DEBUG(4, ("Sending ID to Proxy Backend: (%d,%ld)\n",
+ version, id));
+
+ ret = dbus_message_append_args(msg,
+ DBUS_TYPE_UINT16, &version,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID);
+ if (!ret) {
+ DEBUG(1, ("Failed to build message\n"));
+ return EIO;
+ }
+
+ retval = sbus_conn_send(conn, msg, 30000, dp_id_callback, NULL, NULL);
+
+ dbus_message_unref(msg);
+ return retval;
+}
+
+int proxy_child_process_init(TALLOC_CTX *mem_ctx, const char *domain,
+ struct tevent_context *ev, struct confdb_ctx *cdb,
+ const char *pam_target, uint32_t id)
+{
+ struct pc_ctx *ctx;
+ int ret;
+
+ ctx = talloc_zero(mem_ctx, struct pc_ctx);
+ if (!ctx) {
+ DEBUG(0, ("fatal error initializing pc_ctx\n"));
+ return ENOMEM;
+ }
+ ctx->ev = ev;
+ ctx->cdb = cdb;
+ ctx->pam_target = talloc_steal(ctx, pam_target);
+ ctx->id = id;
+ ctx->conf_path = talloc_asprintf(ctx, CONFDB_DOMAIN_PATH_TMPL, domain);
+ if (!ctx->conf_path) {
+ DEBUG(0, ("Out of memory!?\n"));
+ return ENOMEM;
+ }
+
+ ret = confdb_get_domain(cdb, domain, &ctx->domain);
+ if (ret != EOK) {
+ DEBUG(0, ("fatal error retrieving domain configuration\n"));
+ return ret;
+ }
+
+ ret = proxy_cli_init(ctx);
+ if (ret != EOK) {
+ DEBUG(0, ("fatal error setting up server bus\n"));
+ return ret;
+ }
+
+ return EOK;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *domain = NULL;
+ char *srv_name = NULL;
+ char *conf_entry = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ long id;
+ char *pam_target = NULL;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ {"domain", 0, POPT_ARG_STRING, &domain, 0,
+ _("Domain of the information provider (mandatory)"), NULL },
+ {"id", 0, POPT_ARG_LONG, &id, 0,
+ _("Child identifier (mandatory)"), NULL },
+ POPT_TABLEEND
+ };
+
+ 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 (domain == NULL) {
+ fprintf(stderr, "\nMissing option, "
+ "--domain is a mandatory option.\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ if (id == 0) {
+ fprintf(stderr, "\nMissing option, "
+ "--id is a mandatory option.\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+
+
+ /* set up things like debug , signals, daemonization, etc... */
+ debug_log_file = talloc_asprintf(NULL, "proxy_child_%s", domain);
+ if (!debug_log_file) return 2;
+
+ srv_name = talloc_asprintf(NULL, "sssd[proxy_child[%s]]", domain);
+ if (!srv_name) return 2;
+
+ conf_entry = talloc_asprintf(NULL, CONFDB_DOMAIN_PATH_TMPL, domain);
+ if (!conf_entry) return 2;
+
+ ret = server_setup(srv_name, 0, conf_entry, &main_ctx);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not set up mainloop [%d]\n", ret));
+ return 2;
+ }
+
+ ret = unsetenv("_SSS_LOOPS");
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to unset _SSS_LOOPS, "
+ "pam modules might not work as expected.\n"));
+ }
+
+ ret = confdb_get_string(main_ctx->confdb_ctx, main_ctx, conf_entry,
+ CONFDB_PROXY_PAM_TARGET, NULL, &pam_target);
+ if (ret != EOK) {
+ DEBUG(0, ("Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret)));
+ return 4;
+ }
+ if (pam_target == NULL) {
+ DEBUG(1, ("Missing option proxy_pam_target.\n"));
+ return 4;
+ }
+
+ 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 = proxy_child_process_init(main_ctx, domain, main_ctx->event_ctx,
+ main_ctx->confdb_ctx, pam_target,
+ (uint32_t)id);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not initialize proxy child [%d].\n", ret));
+ return 3;
+ }
+
+ DEBUG(1, ("Proxy child for domain [%s] started!\n", domain));
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}