diff options
author | Sumit Bose <sbose@redhat.com> | 2009-02-24 19:28:40 -0500 |
---|---|---|
committer | Simo Sorce <ssorce@redhat.com> | 2009-02-24 21:00:56 -0500 |
commit | 98531e56318b65eb1bb6883fdfe12e771d8a1efe (patch) | |
tree | a339a5948604ff62cfafd62a9682130f30df689e /server/providers | |
parent | 4c6c0f77a505b6b0790cfa8eedd3133abebd4edb (diff) | |
download | sssd-98531e56318b65eb1bb6883fdfe12e771d8a1efe.tar.gz sssd-98531e56318b65eb1bb6883fdfe12e771d8a1efe.tar.bz2 sssd-98531e56318b65eb1bb6883fdfe12e771d8a1efe.zip |
Add PAM responder
Also move responders under server/responder with shared code
in server/responder/common
Signed-off-by: Simo Sorce <ssorce@redhat.com>
Diffstat (limited to 'server/providers')
-rw-r--r-- | server/providers/data_provider.c | 274 | ||||
-rw-r--r-- | server/providers/data_provider.h | 6 | ||||
-rw-r--r-- | server/providers/data_provider_be.c | 138 | ||||
-rw-r--r-- | server/providers/dp_backend.h | 8 | ||||
-rw-r--r-- | server/providers/ldap_be.c | 676 | ||||
-rw-r--r-- | server/providers/proxy.c | 132 |
6 files changed, 1232 insertions, 2 deletions
diff --git a/server/providers/data_provider.c b/server/providers/data_provider.c index c6dc8d12..a0e3b08d 100644 --- a/server/providers/data_provider.c +++ b/server/providers/data_provider.c @@ -29,6 +29,8 @@ #include <string.h> #include <sys/time.h> #include <errno.h> +#include <security/pam_modules.h> + #include "popt.h" #include "util/util.h" #include "confdb/confdb.h" @@ -39,6 +41,7 @@ #include "dp_interfaces.h" #include "monitor/monitor_sbus.h" #include "monitor/monitor_interfaces.h" +#include "responder/pam/pamsrv.h" struct dp_backend; struct dp_frontend; @@ -88,9 +91,11 @@ struct sbus_method mon_sbus_methods[] = { }; static int dp_get_account_info(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int dp_pamhandler(DBusMessage *message, struct sbus_conn_ctx *sconn); struct sbus_method dp_sbus_methods[] = { { DP_SRV_METHOD_GETACCTINFO, dp_get_account_info }, + { DP_SRV_METHOD_PAMHANDLER, dp_pamhandler }, { NULL, NULL } }; @@ -721,6 +726,275 @@ respond: return EOK; } +static void be_got_pam_reply(DBusPendingCall *pending, void *data) +{ + struct dp_be_request *bereq; + DBusMessage *reply; + DBusConnection *conn; + DBusError dbus_error; + dbus_bool_t ret; + uint32_t pam_status; + char *domain; + int type; + + bereq = talloc_get_type(data, struct dp_be_request); + dbus_error_init(&dbus_error); + + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); + + /* Destroy this connection */ + sbus_disconnect(bereq->be->dpcli->conn_ctx); + goto done; + } + + type = dbus_message_get_type(reply); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + ret = dbus_message_get_args(reply, &dbus_error, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &domain, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to parse message, killing connection\n")); + if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); + sbus_disconnect(bereq->be->dpcli->conn_ctx); + pam_status = PAM_SYSTEM_ERR; + domain = ""; + goto done; + } + + DEBUG(4, ("Got reply (%d, %s) from %s(%s)\n", pam_status, domain, + bereq->be->name, bereq->be->domain)); + + break; + + case DBUS_MESSAGE_TYPE_ERROR: + DEBUG(0,("The Data Provider returned an error [%s], closing connection.\n", + dbus_message_get_error_name(reply))); + /* Falling through to default intentionally*/ + default: + /* + * Timeout or other error occurred or something + * unexpected happened. + * It doesn't matter which, because either way we + * know that this connection isn't trustworthy. + * We'll destroy it now. + */ + DEBUG(1,("Maybe timeout?\n")); + sbus_disconnect(bereq->be->dpcli->conn_ctx); + goto done; + } + + conn = sbus_get_connection(bereq->req->src_cli->conn_ctx); + ret = dbus_message_append_args(bereq->req->reply, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &domain, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1, ("Failed to build reply ... frontend will wait for timeout ...\n")); + talloc_free(bereq->req); + goto done; + } + + /* finally send it */ + dbus_connection_send(conn, bereq->req->reply, NULL); + dbus_message_unref(bereq->req->reply); + talloc_free(bereq->req); + +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); +} + +static int dp_call_pamhandler(struct dp_be_request *bereq, struct pam_data *pd) +{ + DBusMessage *msg; + DBusPendingCall *pending_reply; + DBusConnection *conn; + dbus_bool_t ret; + + conn = sbus_get_connection(bereq->be->dpcli->conn_ctx); + + /* create the message */ + msg = dbus_message_new_method_call(NULL, + DP_CLI_PATH, + DP_CLI_INTERFACE, + DP_CLI_METHOD_PAMHANDLER); + if (msg == NULL) { + DEBUG(0,("Out of memory?!\n")); + return ENOMEM; + } + + DEBUG(4, ("Sending request with to following data\n")); + DEBUG_PAM_DATA(4, pd); + + ret = dbus_message_append_args(msg, + DBUS_TYPE_INT32, &(pd->cmd), + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_STRING, &(pd->user), + DBUS_TYPE_STRING, &(pd->service), + DBUS_TYPE_STRING, &(pd->tty), + DBUS_TYPE_STRING, &(pd->ruser), + DBUS_TYPE_STRING, &(pd->rhost), + DBUS_TYPE_INT32, &(pd->authtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->authtok), + (pd->authtok_size), + DBUS_TYPE_INT32, &(pd->newauthtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->newauthtok), + pd->newauthtok_size, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to build message\n")); + return EIO; + } + + ret = dbus_connection_send_with_reply(conn, msg, &pending_reply, + 600000 /* TODO: set timeout */); + if (!ret) { + /* + * Critical Failure + * We can't communicate on this connection + * We'll drop it using the default destructor. + */ + DEBUG(0, ("D-BUS send failed.\n")); + dbus_message_unref(msg); + return EIO; + } + + /* Set up the reply handler */ + dbus_pending_call_set_notify(pending_reply, be_got_pam_reply, + bereq, NULL); + dbus_message_unref(msg); + + return EOK; +} + +static int dp_pamhandler(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + DBusMessage *reply; + DBusError dbus_error; + struct dp_client *dpcli; + struct dp_backend *dpbe; + struct dp_be_request *bereq; + struct dp_request *dpreq = NULL; + dbus_bool_t dbret; + void *user_data; + int ret; + struct pam_data *pd; + int pam_status=PAM_SUCCESS; + int domain_found=0; + + user_data = sbus_conn_get_private_data(sconn); + if (!user_data) return EINVAL; + dpcli = talloc_get_type(user_data, struct dp_client); + if (!dpcli) return EINVAL; + +/* FIXME: free arrays returned by dbus_message_get_args() */ + pd = talloc(NULL, struct pam_data); + if (!pd) return ENOMEM; + + dbus_error_init(&dbus_error); + + ret = dbus_message_get_args(message, &dbus_error, + DBUS_TYPE_INT32, &(pd->cmd), + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_STRING, &(pd->user), + DBUS_TYPE_STRING, &(pd->service), + DBUS_TYPE_STRING, &(pd->tty), + DBUS_TYPE_STRING, &(pd->ruser), + DBUS_TYPE_STRING, &(pd->rhost), + DBUS_TYPE_INT32, &(pd->authtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->authtok), + &(pd->authtok_size), + DBUS_TYPE_INT32, &(pd->newauthtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->newauthtok), + &(pd->newauthtok_size), + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(0,("Failed, to parse message!\n")); + if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); + talloc_free(pd); + return EIO; + } + + DEBUG(4, ("Got the following data:\n")); + DEBUG_PAM_DATA(4, pd); + + reply = dbus_message_new_method_return(message); + if (!reply) { + DEBUG(0,("Out of memory?!\n")); + talloc_free(pd); + return ENOMEM; + } + + dpreq = talloc(dpcli->dpctx, struct dp_request); + if (!dpreq) { + ret = ENOMEM; + pam_status = PAM_ABORT; + goto respond; + } + + dpreq->reply = reply; + dpreq->src_cli = dpcli; + dpreq->pending_replies = 0; + /* FIXME: add handling of default domain */ + dpbe = dpcli->dpctx->be_list; + while (dpbe) { + DEBUG(4, ("Checking [%s][%s]\n", pd->domain, dpbe->domain)); + if (strcasecmp(dpbe->domain, pd->domain) == 0 ) { + domain_found=1; + bereq = talloc(dpreq, struct dp_be_request); + if (!bereq) { + DEBUG(1, ("Out of memory while sending requests\n")); + dpbe = dpbe->next; + continue; + } + bereq->req = dpreq; + bereq->be = dpbe; + DEBUG(4, ("Sending wildcard request to [%s]\n", dpbe->domain)); + ret = dp_call_pamhandler(bereq, pd); + if (ret != EOK) { + DEBUG(2,("Failed to dispatch request to %s\n", dpbe->domain)); + dpbe = dpbe->next; + continue; + } + dpreq->pending_replies++; + } + dpbe = dpbe->next; + } + + if (domain_found) { + talloc_free(pd); + return EOK; + } + + pam_status = PAM_MODULE_UNKNOWN; + +respond: + dbret = dbus_message_append_args(reply, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_INVALID); + if (!dbret) return EIO; + + /* send reply back immediately */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + talloc_free(pd); + return EOK; +} + static int dp_backend_destructor(void *ctx) { struct dp_backend *dpbe = talloc_get_type(ctx, struct dp_backend); diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h index 5dc402ef..2d0e37e1 100644 --- a/server/providers/data_provider.h +++ b/server/providers/data_provider.h @@ -52,11 +52,17 @@ #define DP_CLI_FRONTEND 0x0002 #define DP_CLI_TYPE_MASK 0x0003 +#define DP_CLI_PROVIDE_ACC_INFO (1<<8) +#define DP_CLI_PROVIDE_PAM (1<<9) +#define DP_CLI_PROVIDE_POLICY (1<<10) + #define DP_CLI_METHOD_IDENTITY "getIdentity" #define DP_CLI_METHOD_ONLINE "getOnline" #define DP_CLI_METHOD_GETACCTINFO "getAccountInfo" +#define DP_CLI_METHOD_PAMHANDLER "pamHandler" #define DP_SRV_METHOD_GETACCTINFO "getAccountInfo" +#define DP_SRV_METHOD_PAMHANDLER "pamHandler" #define DP_ERR_OK 0 #define DP_ERR_OFFLINE 1 diff --git a/server/providers/data_provider_be.c b/server/providers/data_provider_be.c index 26a78efb..9ed3d2ce 100644 --- a/server/providers/data_provider_be.c +++ b/server/providers/data_provider_be.c @@ -29,6 +29,10 @@ #include <string.h> #include <sys/time.h> #include <errno.h> + +#include <security/pam_appl.h> +#include <security/pam_modules.h> + #include "popt.h" #include "util/util.h" #include "confdb/confdb.h" @@ -40,7 +44,7 @@ #include "providers/dp_sbus.h" #include "monitor/monitor_sbus.h" #include "monitor/monitor_interfaces.h" - +#include "../sss_client/sss_cli.h" typedef int (*be_init_fn_t)(TALLOC_CTX *, struct be_mod_ops **, void **); @@ -56,11 +60,13 @@ struct sbus_method mon_sbus_methods[] = { static int be_identity(DBusMessage *message, struct sbus_conn_ctx *sconn); static int be_check_online(DBusMessage *message, struct sbus_conn_ctx *sconn); static int be_get_account_info(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int be_pam_handler(DBusMessage *message, struct sbus_conn_ctx *sconn); struct sbus_method be_methods[] = { { DP_CLI_METHOD_IDENTITY, be_identity }, { DP_CLI_METHOD_ONLINE, be_check_online }, { DP_CLI_METHOD_GETACCTINFO, be_get_account_info }, + { DP_CLI_METHOD_PAMHANDLER, be_pam_handler }, { NULL, NULL } }; @@ -503,8 +509,138 @@ done: return EOK; } +static void be_pam_handler_callback(struct be_req *req, int status, + const char *errstr) { + struct be_pam_handler *ph; + DBusMessage *reply; + DBusConnection *conn; + dbus_bool_t dbret; + + ph = talloc_get_type(req->req_data, struct be_pam_handler); + + DEBUG(4, ("Sending result [%d][%s]\n", ph->pam_status, ph->domain)); + reply = (DBusMessage *)req->pvt; + dbret = dbus_message_append_args(reply, + DBUS_TYPE_UINT32, &(ph->pam_status), + DBUS_TYPE_STRING, &(ph->domain), + DBUS_TYPE_INVALID); + if (!dbret) { + DEBUG(1, ("Failed to generate dbus reply\n")); + return; + } + + conn = sbus_get_connection(req->be_ctx->dp_ctx->scon_ctx); + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + + DEBUG(4, ("Sent result [%d][%s]\n", ph->pam_status, ph->domain)); + + talloc_free(req); +} + +static int be_pam_handler(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + DBusError dbus_error; + DBusMessage *reply; + struct be_ctx *ctx; + struct be_pam_handler *req; + struct be_req *be_req; + dbus_bool_t ret; + void *user_data; + struct pam_data *pd; + uint32_t pam_status=99; + + user_data = sbus_conn_get_private_data(sconn); + if (!user_data) return EINVAL; + ctx = talloc_get_type(user_data, struct be_ctx); + if (!ctx) return EINVAL; + + pd = talloc(NULL, struct pam_data); + if (!pd) return ENOMEM; + + dbus_error_init(&dbus_error); + + reply = dbus_message_new_method_return(message); + if (!reply) { + DEBUG(1, ("dbus_message_new_method_return failed, cannot send reply.\n")); + talloc_free(pd); + return ENOMEM; + } + ret = dbus_message_get_args(message, &dbus_error, + DBUS_TYPE_INT32, &(pd->cmd), + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_STRING, &(pd->user), + DBUS_TYPE_STRING, &(pd->service), + DBUS_TYPE_STRING, &(pd->tty), + DBUS_TYPE_STRING, &(pd->ruser), + DBUS_TYPE_STRING, &(pd->rhost), + DBUS_TYPE_INT32, &(pd->authtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->authtok), + &(pd->authtok_size), + DBUS_TYPE_INT32, &(pd->newauthtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->newauthtok), + &(pd->newauthtok_size), + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed, to parse message!\n")); + talloc_free(pd); + return EIO; + } + + DEBUG(4, ("Got request with the following data\n")); + DEBUG_PAM_DATA(4, pd); + + be_req = talloc(ctx, struct be_req); + if (!be_req) { + pam_status = PAM_SYSTEM_ERR; + goto done; + } + be_req->be_ctx = ctx; + be_req->fn = be_pam_handler_callback; + be_req->pvt = reply; + + req = talloc(be_req, struct be_pam_handler); + if (!req) { + pam_status = PAM_SYSTEM_ERR; + goto done; + } + req->domain = ctx->domain; + req->pd = pd; + + be_req->req_data = req; + + ret = be_file_request(ctx, ctx->ops->pam_handler, be_req); + if (ret != EOK) { + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + return EOK; + +done: + if (be_req) { + talloc_free(be_req); + } + + DEBUG(4, ("Sending result [%d][%s]\n", pam_status, ctx->domain)); + ret = dbus_message_append_args(reply, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &ctx->domain, + DBUS_TYPE_INVALID); + if (!ret) return EIO; + + /* send reply back immediately */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + talloc_free(pd); + return EOK; +} + /* mon_cli_init * sbus channel to the monitor daemon */ static int mon_cli_init(struct be_ctx *ctx) diff --git a/server/providers/dp_backend.h b/server/providers/dp_backend.h index 765c16ee..cdd2c51b 100644 --- a/server/providers/dp_backend.h +++ b/server/providers/dp_backend.h @@ -24,6 +24,7 @@ #include "providers/data_provider.h" #include "db/sysdb.h" +#include "responder/pam/pamsrv.h" struct be_mod_ops; @@ -64,11 +65,18 @@ struct be_online_req { int online; }; +struct be_pam_handler { + int pam_status; + const char *domain; + struct pam_data *pd; +}; + typedef void (*be_req_fn_t)(struct be_req *); struct be_mod_ops { be_req_fn_t check_online; be_req_fn_t get_account_info; + be_req_fn_t pam_handler; }; #endif /* __DP_BACKEND_H___ */ diff --git a/server/providers/ldap_be.c b/server/providers/ldap_be.c new file mode 100644 index 00000000..6c573b7d --- /dev/null +++ b/server/providers/ldap_be.c @@ -0,0 +1,676 @@ +/* + SSSD + + LDAP Backend Module + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2008 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 <errno.h> +#include <ldap.h> +#include <sys/time.h> + +#include <security/pam_modules.h> + +#include "util/util.h" +#include "providers/dp_backend.h" +#include "db/sysdb.h" +#include "../sss_client/sss_cli.h" + +struct ldap_ctx { + char *ldap_uri; + char *default_bind_dn; + char *default_authtok_type; + uint32_t default_authtok_size; + char *default_authtok; +}; + +struct ldap_ops; +struct ldap_req; + +struct ldap_ops { + void (*op)(struct ldap_req *); + struct ldap_ops *next; +}; + +enum ldap_be_ops { + LDAP_NOOP = 0x0000, + LDAP_OP_INIT = 0x0001, + LDAP_CHECK_INIT_RESULT, + LDAP_CHECK_STD_BIND, + LDAP_CHECK_SEARCH_DN_RESULT, + LDAP_CHECK_USER_BIND +}; + +struct ldap_req { + struct be_req *req; + struct pam_data *pd; + struct ldap_ctx *ldap_ctx; + LDAP *ldap; + struct ldap_ops *ops; + char *user_dn; + /*event_timed_handler_t next_task;*/ + event_fd_handler_t next_task; + enum ldap_be_ops next_op; + int msgid; +}; + +static int schedule_next_task(struct ldap_req *lr, struct timeval tv, + event_timed_handler_t task) { + int ret; + struct timed_event *te; + struct timeval timeout; + + ret = gettimeofday(&timeout, NULL); + if (ret == -1) { + DEBUG(1, ("gettimeofday failed [%d][%s].\n", errno, strerror(errno))); + return ret; + } + timeout.tv_sec += tv.tv_sec; + timeout.tv_usec += tv.tv_usec; + + + te = event_add_timed(lr->req->be_ctx->ev, lr, timeout, task, lr); + if ( te == NULL ) { + return EIO; + } + + return EOK; +} + +static int wait_for_fd(struct ldap_req *lr) { + int ret; + int fd; + struct fd_event *fde; + + ret = ldap_get_option(lr->ldap, LDAP_OPT_DESC, &fd); + if ( ret != LDAP_OPT_SUCCESS ) { + DEBUG(1, ("ldap_get_option failed.\n")); + return ret; + } + + fde = event_add_fd(lr->req->be_ctx->ev, lr, fd, EVENT_FD_READ, lr->next_task, lr); + if ( fde == NULL ) { + return EIO; + } + + return EOK; +} + +static int ldap_pam_chauthtok(struct ldap_req *lr) { + BerElement *ber=NULL; + int ret; + int pam_status=PAM_SUCCESS; + struct berval *bv; + int msgid; + LDAPMessage *result=NULL; + int ldap_ret; + + ber = ber_alloc_t( LBER_USE_DER ); + if ( ber == NULL ) { + DEBUG(1, ("ber_alloc_t failed.\n")); + return PAM_SYSTEM_ERR; + } + + ret = ber_printf( ber, "{tststs}", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, + lr->user_dn, + LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, lr->pd->authtok, + LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, lr->pd->newauthtok); + if ( ret == -1 ) { + DEBUG(1, ("ber_printf failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + + ret = ber_flatten(ber, &bv); + if ( ret == -1 ) { + DEBUG(1, ("ber_flatten failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + + ret = ldap_extended_operation(lr->ldap, LDAP_EXOP_MODIFY_PASSWD, bv, + NULL, NULL, &msgid); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_extended_operation failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + + ret = ldap_result(lr->ldap, msgid, FALSE, NULL, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, NULL, NULL, + NULL, 0); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_parse_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + DEBUG(3, ("LDAP_EXOP_MODIFY_PASSWD result: [%d][%s]\n", ldap_ret, + ldap_err2string(ldap_ret))); + + ldap_msgfree(result); + + if ( ldap_ret != LDAP_SUCCESS ) pam_status = PAM_SYSTEM_ERR; + +cleanup: + ber_bvfree(bv); + ber_free(ber, 1); + return pam_status; +} + +static int ldap_be_init(struct ldap_req *lr) { + int ret; + int status=EOK; + int ldap_vers = LDAP_VERSION3; + int msgid; + + ret = ldap_initialize(&(lr->ldap), lr->ldap_ctx->ldap_uri); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_initialize failed: %s\n", strerror(errno))); + return EIO; + } + + /* LDAPv3 is needed for TLS */ + ret = ldap_set_option(lr->ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_vers); + if ( ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret))); + status = EIO; + goto cleanup; + } + + /* For now TLS is forced. Maybe it would be necessary to make this + * configurable to allow people to expose their passwords over the + * network. */ + ret = ldap_start_tls(lr->ldap, NULL, NULL, &msgid); + if ( ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_start_tls failed: %s\n", ldap_err2string(ret))); + status = EIO; + goto cleanup; + } + + lr->msgid = msgid; + + return EOK; + +cleanup: + ldap_unbind_ext(lr->ldap, NULL, NULL); + lr->ldap = NULL; + return status; +} + +static int ldap_be_bind(struct ldap_req *lr) { + int ret; + int msgid; + char *dn=NULL; + struct berval pw; + + pw.bv_len = 0; + pw.bv_val = NULL; + + if ( lr->user_dn != NULL ) { + dn = lr->user_dn; + pw.bv_len = lr->pd->authtok_size; + pw.bv_val = (char *) lr->pd->authtok; + } + if ( lr->user_dn == NULL && lr->ldap_ctx->default_bind_dn != NULL ) { + dn = lr->ldap_ctx->default_bind_dn; + pw.bv_len = lr->ldap_ctx->default_authtok_size; + pw.bv_val = lr->ldap_ctx->default_authtok; + } + + DEBUG(3, ("Trying to bind as [%s][%*s]\n", dn, pw.bv_len, pw.bv_val)); + ret = ldap_sasl_bind(lr->ldap, dn, LDAP_SASL_SIMPLE, &pw, NULL, NULL, + &msgid); + if ( ret == -1 || msgid == -1 ) { + DEBUG(1, ("ldap_bind failed\n")); + return LDAP_OTHER; + } + lr->msgid = msgid; + return LDAP_SUCCESS; +} + +static void ldap_be_loop(struct event_context *ev, struct fd_event *te, + uint16_t fd, void *pvt) { + int ret; + int pam_status=PAM_SUCCESS; + int ldap_ret; + struct ldap_req *lr; + struct be_pam_handler *ph; + struct be_req *req; + LDAPMessage *result=NULL; + LDAPMessage *msg=NULL; + struct timeval no_timeout={0, 0}; + char *errmsgp = NULL; +/* FIXME: user timeout form config */ + char *filter=NULL; + char *attrs[] = { LDAP_NO_ATTRS, NULL }; + + lr = talloc_get_type(pvt, struct ldap_req); + + switch ( lr->next_op ) { + case LDAP_OP_INIT: + ret = ldap_be_init(lr); + if (ret != EOK) { + DEBUG(1, ("ldap_be_init failed.\n")); + lr->ldap = NULL; + pam_status = PAM_SYSTEM_ERR; + goto done; + } + case LDAP_CHECK_INIT_RESULT: + ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + if ( ret == 0 ) { + DEBUG(1, ("ldap_result not ready yet, waiting.\n")); + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_INIT_RESULT; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + } + + ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, NULL, NULL, NULL, 0); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_parse_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + DEBUG(3, ("ldap_start_tls result: [%d][%s]\n", ldap_ret, ldap_err2string(ldap_ret))); + + if ( ldap_ret != LDAP_SUCCESS ) { + DEBUG(1, ("setting up TLS failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + +/* FIXME: take care that ldap_install_tls might block */ + ret = ldap_install_tls(lr->ldap); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_install_tls failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + ret = ldap_be_bind(lr); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_be_bind failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + case LDAP_CHECK_STD_BIND: + ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + if ( ret == 0 ) { + DEBUG(1, ("ldap_result not ready yet, waiting.\n")); + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_STD_BIND; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + } + + ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, &errmsgp, + NULL, NULL, 0); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_parse_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + DEBUG(3, ("Bind result: [%d][%s][%s]\n", ldap_ret, + ldap_err2string(ldap_ret), errmsgp)); + if ( ldap_ret != LDAP_SUCCESS ) { + DEBUG(1, ("bind failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + filter = talloc_asprintf(lr->ldap_ctx, + "(&(uid=%s)(objectclass=posixAccount))", + lr->pd->user); + + DEBUG(4, ("calling ldap_search_ext with [%s].\n", filter)); + ret = ldap_search_ext(lr->ldap, + "ou=user,dc=my-domain,dc=com", + LDAP_SCOPE_SUBTREE, + filter, + attrs, + TRUE, + NULL, + NULL, + NULL, + 0, + &(lr->msgid)); + if ( ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_search_ext failed [%d][%s].\n", ret, ldap_err2string(ret))); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + case LDAP_CHECK_SEARCH_DN_RESULT: + ret = ldap_result(lr->ldap, lr->msgid, TRUE, &no_timeout, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + if ( ret == 0 ) { + DEBUG(1, ("ldap_result not ready yet, waiting.\n")); + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_SEARCH_DN_RESULT; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + } + + msg = ldap_first_message(lr->ldap, result); + if ( msg == NULL ) { + DEBUG(1, ("ldap_first_message failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + do { + switch ( ldap_msgtype(msg) ) { + case LDAP_RES_SEARCH_ENTRY: + if ( lr->user_dn != NULL ) { + DEBUG(1, ("Found more than one object with filter [%s].\n", + filter)); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + lr->user_dn = ldap_get_dn(lr->ldap, msg); + if ( lr->user_dn == NULL ) { + DEBUG(1, ("ldap_get_dn failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + if ( *(lr->user_dn) == '\0' ) { + DEBUG(1, ("No user found.\n")); + pam_status = PAM_USER_UNKNOWN; + goto done; + } + DEBUG(3, ("Found dn: %s\n",lr->user_dn)); + + ldap_msgfree(result); + result = NULL; + break; + default: + DEBUG(3, ("ignoring message with type %d.\n", ldap_msgtype(msg))); + } + } while( (msg=ldap_next_message(lr->ldap, msg)) != NULL ); + + ret = ldap_be_bind(lr); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_be_bind failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + case LDAP_CHECK_USER_BIND: + ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + if ( ret == 0 ) { + DEBUG(1, ("ldap_result not ready yet, waiting.\n")); + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_USER_BIND; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + } + + ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, &errmsgp, + NULL, NULL, 0); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_parse_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + DEBUG(3, ("Bind result: [%d][%s][%s]\n", ldap_ret, + ldap_err2string(ldap_ret), errmsgp)); + switch (ldap_ret) { + case LDAP_SUCCESS: + pam_status = PAM_SUCCESS; + break; + case LDAP_INVALID_CREDENTIALS: + pam_status = PAM_CRED_INSUFFICIENT; + goto done; + break; + default: + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + + + +/* + ret = pam_setup_ldap_connection(lr); + if ( ret != PAM_SUCCESS ) { + DEBUG(1, ("pam_setup_ldap_connection failed.\n")); + pam_status = ret; + goto done; + } + DEBUG(3, ("Successfully connected as %s.\n", lr->user_dn)); +*/ + + switch (lr->pd->cmd) { + case SSS_PAM_AUTHENTICATE: + pam_status = PAM_SUCCESS; + break; + case SSS_PAM_CHAUTHTOK: + pam_status = ldap_pam_chauthtok(lr); + break; + case SSS_PAM_ACCT_MGMT: + case SSS_PAM_SETCRED: + case SSS_PAM_OPEN_SESSION: + case SSS_PAM_CLOSE_SESSION: + pam_status = PAM_SUCCESS; + break; + default: + DEBUG(1, ("Unknown pam command %d.\n", lr->pd->cmd)); + pam_status = PAM_SYSTEM_ERR; + } + break; + default: + DEBUG(1, ("Unknown ldap backend operation %d.\n", lr->next_op)); + pam_status = PAM_SYSTEM_ERR; + } + +done: + ldap_memfree(errmsgp); + ldap_msgfree(result); + talloc_free(filter); + if (lr->ldap != NULL ) ldap_unbind_ext(lr->ldap, NULL, NULL); + req = lr->req; + ph = talloc_get_type(lr->req->req_data, struct be_pam_handler); + ph->pam_status = pam_status; + + talloc_free(lr); + + req->fn(req, pam_status, NULL); +} + +static void ldap_start(struct event_context *ev, struct timed_event *te, + struct timeval tv, void *pvt) { + int ret; + int pam_status; + struct ldap_req *lr; + struct be_req *req; + struct be_pam_handler *ph; + + lr = talloc_get_type(pvt, struct ldap_req); + + ret = ldap_be_init(lr); + if (ret != EOK) { + DEBUG(1, ("ldap_be_init failed.\n")); + lr->ldap = NULL; + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_INIT_RESULT; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + +done: + if (lr->ldap != NULL ) ldap_unbind_ext(lr->ldap, NULL, NULL); + req = lr->req; + ph = talloc_get_type(lr->req->req_data, struct be_pam_handler); + ph->pam_status = pam_status; + + talloc_free(lr); + + req->fn(req, pam_status, NULL); +} + +static void ldap_pam_handler(struct be_req *req) { + int ret; + int pam_status=PAM_SUCCESS; + struct ldap_req *lr; + struct ldap_ctx *ldap_ctx; + struct be_pam_handler *ph; + struct pam_data *pd; + struct timeval timeout; + + ph = talloc_get_type(req->req_data, struct be_pam_handler); + pd = ph->pd; + + ldap_ctx = talloc_get_type(req->be_ctx->pvt_data, struct ldap_ctx); + + lr = talloc(req, struct ldap_req); + + lr->ldap = NULL; + lr->req = req; + lr->pd = pd; + lr->ldap_ctx = ldap_ctx; + lr->user_dn = NULL; + lr->next_task = NULL; + lr->next_op = LDAP_NOOP; + + timeout.tv_sec=0; + timeout.tv_usec=0; + ret = schedule_next_task(lr, timeout, ldap_start); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + return; + +done: + talloc_free(lr); + + ph->pam_status = pam_status; + req->fn(req, pam_status, NULL); +} + +struct be_mod_ops ldap_mod_ops = { + .check_online = NULL, + .get_account_info = NULL, + .pam_handler = ldap_pam_handler +}; + + +int sssm_ldap_init(struct be_ctx *bectx, struct be_mod_ops **ops, void **pvt_data) +{ + struct ldap_ctx *ctx; + char *ldap_uri; + char *default_bind_dn; + char *default_authtok_type; + char *default_authtok; + int ret; + + ctx = talloc(bectx, struct ldap_ctx); + if (!ctx) { + return ENOMEM; + } + + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "ldapUri", "ldap://localhost", &ldap_uri); + if (ret != EOK) goto done; + ctx->ldap_uri = ldap_uri; + + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "defaultBindDn", NULL, &default_bind_dn); + if (ret != EOK) goto done; + ctx->default_bind_dn = default_bind_dn; + + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "defaultAuthtokType", NULL, &default_authtok_type); + if (ret != EOK) goto done; + ctx->default_authtok_type = default_authtok_type; + + +/* TODO: better to have a blob object than a string here */ + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "defaultAuthtok", NULL, &default_authtok); + if (ret != EOK) goto done; + ctx->default_authtok = default_authtok; + ctx->default_authtok_size = (default_authtok==NULL?0:strlen(default_authtok)); + + + + *ops = &ldap_mod_ops; + *pvt_data = ctx; + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(ctx); + } + return ret; +} diff --git a/server/providers/proxy.c b/server/providers/proxy.c index 0ec6e530..e072ec3c 100644 --- a/server/providers/proxy.c +++ b/server/providers/proxy.c @@ -23,9 +23,14 @@ #include <errno.h> #include <pwd.h> #include <grp.h> + +#include <security/pam_appl.h> +#include <security/pam_modules.h> + #include "util/util.h" #include "providers/dp_backend.h" #include "db/sysdb.h" +#include "../sss_client/sss_cli.h" struct proxy_nss_ops { enum nss_status (*getpwnam_r)(const char *name, struct passwd *result, @@ -154,6 +159,130 @@ static void get_pw_uid(struct be_req *req, uid_t uid) return proxy_reply(req, EOK, NULL); } +struct authtok_conv { + char *authtok; + char *oldauthtok; +}; + +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 = strdup(auth_data->authtok); + 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 void proxy_pam_handler(struct be_req *req) { + int ret; + int pam_status; + pam_handle_t *pamh=NULL; + struct authtok_conv *auth_data; + struct pam_conv conv; + struct be_pam_handler *ph; + struct pam_data *pd; + + ph = talloc_get_type(req->req_data, struct be_pam_handler); + pd = ph->pd; + + conv.conv=proxy_internal_conv; + auth_data = talloc_zero(req->be_ctx, struct authtok_conv); + conv.appdata_ptr=auth_data; + + ret = pam_start("sssd_be_test", pd->user, &conv, &pamh); + if (ret == PAM_SUCCESS) { + DEBUG(1, ("Pam transaction started.\n")); + pam_set_item(pamh, PAM_TTY, pd->tty); + if (ret != PAM_SUCCESS) { + DEBUG(1, ("Setting PAM_TTY failed: %s.\n", pam_strerror(pamh, 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))); + } + 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: +/* FIXME: \0 missing at the end */ + auth_data->authtok=(char *) pd->authtok; + auth_data->oldauthtok=NULL; + 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: +/* FIXME: \0 missing at the end */ + auth_data->authtok=(char *) pd->newauthtok; + auth_data->oldauthtok=(char *) pd->authtok; + pam_status=pam_chauthtok(pamh, 0); + break; + default: + DEBUG(1, ("unknown PAM call")); + 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; + } + + talloc_free(auth_data); + + ph->pam_status = pam_status; + req->fn(req, EOK, NULL); +} + #define MAX_BUF_SIZE 1024*1024 /* max 1MiB */ static void enum_users(struct be_req *req) @@ -664,7 +793,8 @@ static void proxy_get_account_info(struct be_req *req) struct be_mod_ops proxy_mod_ops = { .check_online = proxy_check_online, - .get_account_info = proxy_get_account_info + .get_account_info = proxy_get_account_info, + .pam_handler = proxy_pam_handler }; static void *proxy_dlsym(void *handle, const char *functemp, char *libname) |