diff options
-rw-r--r-- | server/examples/sssdproxylocal | 6 | ||||
-rw-r--r-- | server/examples/sssdproxytest | 8 | ||||
-rw-r--r-- | server/providers/data_provider.c | 1 | ||||
-rw-r--r-- | server/providers/data_provider.h | 47 | ||||
-rw-r--r-- | server/providers/dp_auth_util.c (renamed from server/responder/pam/pamsrv_util.c) | 24 | ||||
-rw-r--r-- | server/providers/dp_backend.h | 1 | ||||
-rw-r--r-- | server/responder/pam/pam_LOCAL_domain.c | 153 | ||||
-rw-r--r-- | server/responder/pam/pam_LOCAL_domain.h | 9 | ||||
-rw-r--r-- | server/responder/pam/pamsrv.c | 3 | ||||
-rw-r--r-- | server/responder/pam/pamsrv.h | 53 | ||||
-rw-r--r-- | server/responder/pam/pamsrv_cache.c | 275 | ||||
-rw-r--r-- | server/responder/pam/pamsrv_cmd.c | 123 | ||||
-rw-r--r-- | server/responder/pam/pamsrv_dp.c | 63 | ||||
-rw-r--r-- | server/server.mk | 19 |
14 files changed, 576 insertions, 209 deletions
diff --git a/server/examples/sssdproxylocal b/server/examples/sssdproxylocal index 1bc47f89..063dbff3 100644 --- a/server/examples/sssdproxylocal +++ b/server/examples/sssdproxylocal @@ -1,9 +1,5 @@ #%PAM-1.0 -auth sufficient pam_unix.so -auth requisite pam_succeed_if.so uid >= 500 quiet -auth required pam_deny.so +auth required pam_unix.so account required pam_unix.so -account sufficient pam_succeed_if.so uid < 500 quiet -account required pam_permit.so diff --git a/server/examples/sssdproxytest b/server/examples/sssdproxytest index 9c5cb4ad..14217969 100644 --- a/server/examples/sssdproxytest +++ b/server/examples/sssdproxytest @@ -1,9 +1,5 @@ #%PAM-1.0 -auth sufficient pam_ldap.so debug -auth requisite pam_succeed_if.so uid >= 1000 quiet -auth required pam_deny.so +auth irequired pam_ldap.so -account required pam_ldap.so debug -account sufficient pam_succeed_if.so uid < 1000 quiet -account required pam_permit.so +account required pam_ldap.so diff --git a/server/providers/data_provider.c b/server/providers/data_provider.c index 4614250c..e8f190ea 100644 --- a/server/providers/data_provider.c +++ b/server/providers/data_provider.c @@ -41,7 +41,6 @@ #include "dp_interfaces.h" #include "monitor/monitor_sbus.h" #include "monitor/monitor_interfaces.h" -#include "responder/pam/pamsrv.h" #define DP_CONF_ENTRY "config/services/dp" diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h index 4b68a0bd..2c828fab 100644 --- a/server/providers/data_provider.h +++ b/server/providers/data_provider.h @@ -34,6 +34,7 @@ #include "sbus/sssd_dbus.h" #include "sbus/sbus_client.h" #include "providers/dp_interfaces.h" +#include "../sss_client/sss_cli.h" #define DATA_PROVIDER_VERSION 0x0001 #define DATA_PROVIDER_SERVICE_NAME "dp" @@ -80,4 +81,50 @@ #define BE_REQ_GROUP 2 #define BE_REQ_INITGROUPS 3 +/* AUTH related common data and functions */ + +#define DEBUG_PAM_DATA(level, pd) do { \ + if (level <= debug_level) pam_print_data(level, pd); \ +} while(0); + + +struct response_data { + int32_t type; + int32_t len; + uint8_t *data; + struct response_data *next; +}; + +struct pam_data { + int cmd; + uint32_t authtok_type; + uint32_t authtok_size; + uint32_t newauthtok_type; + uint32_t newauthtok_size; + char *domain; + char *user; + char *service; + char *tty; + char *ruser; + char *rhost; + uint8_t *authtok; + uint8_t *newauthtok; + + int pam_status; + int response_delay; + struct response_data *resp_list; + + bool offline_auth; +}; + +void pam_print_data(int l, struct pam_data *pd); + +int pam_add_response(struct pam_data *pd, enum response_type type, + int len, const uint8_t *data); + +bool dp_pack_pam_request(DBusMessage *msg, struct pam_data *pd); +bool dp_unpack_pam_request(DBusMessage *msg, struct pam_data *pd, DBusError *dbus_error); +bool dp_pack_pam_response(DBusMessage *msg, struct pam_data *pd); +bool dp_unpack_pam_response(DBusMessage *msg, struct pam_data *pd, DBusError *dbus_error); + #endif /* __DATA_PROVIDER_ */ diff --git a/server/responder/pam/pamsrv_util.c b/server/providers/dp_auth_util.c index ab9b733e..99e57e2e 100644 --- a/server/responder/pam/pamsrv_util.c +++ b/server/providers/dp_auth_util.c @@ -1,5 +1,25 @@ -#include "util/util.h" -#include "responder/pam/pamsrv.h" +/* + SSSD + + Data Provider, auth utils + + Copyright (C) Sumit Bose <sbose@redhat.com> 2009 + + 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 "data_provider.h" void pam_print_data(int l, struct pam_data *pd) { diff --git a/server/providers/dp_backend.h b/server/providers/dp_backend.h index da71e753..27f79eb7 100644 --- a/server/providers/dp_backend.h +++ b/server/providers/dp_backend.h @@ -24,7 +24,6 @@ #include "providers/data_provider.h" #include "db/sysdb.h" -#include "responder/pam/pamsrv.h" struct be_ctx; struct be_id_ops; diff --git a/server/responder/pam/pam_LOCAL_domain.c b/server/responder/pam/pam_LOCAL_domain.c index 7ee84eb6..df2803e5 100644 --- a/server/responder/pam/pam_LOCAL_domain.c +++ b/server/responder/pam/pam_LOCAL_domain.c @@ -1,11 +1,32 @@ +/* + SSSD + + PAM e credentials + + Copyright (C) Sumit Bose <sbose@redhat.com> 2009 + + 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 <time.h> #include <security/pam_modules.h> -#include <ldb.h> #include "util/util.h" -#include "responder/pam/pamsrv.h" #include "db/sysdb.h" #include "util/nss_sha512crypt.h" +#include "providers/data_provider.h" +#include "responder/pam/pamsrv.h" #define NULL_CHECK_OR_JUMP(var, msg, ret, err, label) do { \ @@ -26,15 +47,14 @@ struct LOCAL_request { - struct cli_ctx *cctx; - struct pam_data *pd; - pam_dp_callback_t callback; struct sysdb_ctx *dbctx; - struct sss_domain_info *domain_info; struct sysdb_attrs *mod_attrs; struct sysdb_req *sysdb_req; + struct ldb_result *res; int error; + + struct pam_auth_req *preq; }; static int authtok2str(const void *mem_ctx, uint8_t *src, const int src_size, char **dest) @@ -45,7 +65,7 @@ static int authtok2str(const void *mem_ctx, uint8_t *src, const int src_size, ch } *dest = talloc_size(mem_ctx, src_size + 1); - if (dest == NULL) { + if (*dest == NULL) { return ENOMEM; } memcpy(*dest, src, src_size); @@ -56,12 +76,14 @@ static int authtok2str(const void *mem_ctx, uint8_t *src, const int src_size, ch static void prepare_reply(struct LOCAL_request *lreq) { - if (lreq->error != EOK && lreq->pd->pam_status == PAM_SUCCESS) - lreq->pd->pam_status = PAM_SYSTEM_ERR; + struct pam_data *pd; + + pd = lreq->preq->pd; - lreq->callback(lreq->pd); + if (lreq->error != EOK && pd->pam_status == PAM_SUCCESS) + pd->pam_status = PAM_SYSTEM_ERR; - talloc_free(lreq); + lreq->preq->callback(lreq->preq); } static void set_user_attr_callback(void *pvt, int ldb_status, struct ldb_result *res) @@ -93,8 +115,8 @@ static void set_user_attr_req(struct sysdb_req *req, void *pvt) lreq->sysdb_req = req; - ret = sysdb_set_user_attr(req, lreq->dbctx, lreq->domain_info, - lreq->pd->user, lreq->mod_attrs, + ret = sysdb_set_user_attr(req, lreq->dbctx, lreq->preq->domain, + lreq->preq->pd->user, lreq->mod_attrs, set_user_attr_callback, lreq); if (ret != EOK) sysdb_transaction_done(lreq->sysdb_req, ret); @@ -139,10 +161,12 @@ static void do_failed_login(struct LOCAL_request *lreq) { int ret; int failedLoginAttempts; + struct pam_data *pd; - lreq->pd->pam_status = PAM_AUTH_ERR; + pd = lreq->preq->pd; + pd->pam_status = PAM_AUTH_ERR; /* TODO: maybe add more inteligent delay calculation */ - lreq->pd->response_delay = 3; + pd->response_delay = 3; lreq->mod_attrs = sysdb_new_attrs(lreq); NULL_CHECK_OR_JUMP(lreq->mod_attrs, ("sysdb_new_attrs failed.\n"), @@ -175,14 +199,17 @@ done: static void do_pam_acct_mgmt(struct LOCAL_request *lreq) { - const char *disabled=NULL; + const char *disabled; + struct pam_data *pd; + + pd = lreq->preq->pd; disabled = ldb_msg_find_attr_as_string(lreq->res->msgs[0], SYSDB_DISABLED, NULL); - if (disabled != NULL && - strncasecmp(disabled, "false",5)!=0 && - strncasecmp(disabled, "no",2)!=0 ) { - lreq->pd->pam_status = PAM_PERM_DENIED; + if ((disabled != NULL) && + (strncasecmp(disabled, "false",5) != 0) && + (strncasecmp(disabled, "no",2) != 0) ) { + pd->pam_status = PAM_PERM_DENIED; } prepare_reply(lreq); @@ -194,12 +221,14 @@ static void do_pam_chauthtok(struct LOCAL_request *lreq) char *newauthtok; char *salt; char *new_hash; + struct pam_data *pd; + + pd = lreq->preq->pd; - ret = authtok2str(lreq, lreq->pd->newauthtok, lreq->pd->newauthtok_size, - &newauthtok); + ret = authtok2str(lreq, pd->newauthtok, pd->newauthtok_size, &newauthtok); NEQ_CHECK_OR_JUMP(ret, EOK, ("authtok2str failed.\n"), lreq->error, ret, done); - memset(lreq->pd->newauthtok, 0, lreq->pd->newauthtok_size); + memset(pd->newauthtok, 0, pd->newauthtok_size); salt = gen_salt(); NULL_CHECK_OR_JUMP(salt, ("Salt generation failed.\n"), @@ -210,7 +239,7 @@ static void do_pam_chauthtok(struct LOCAL_request *lreq) NULL_CHECK_OR_JUMP(new_hash, ("Hash generation failed.\n"), lreq->error, EFAULT, done); DEBUG(4, ("New hash [%s]\n", new_hash)); - memset(newauthtok, 0, lreq->pd->newauthtok_size); + memset(newauthtok, 0, pd->newauthtok_size); lreq->mod_attrs = sysdb_new_attrs(lreq); NULL_CHECK_OR_JUMP(lreq->mod_attrs, ("sysdb_new_attrs failed.\n"), @@ -234,8 +263,8 @@ done: prepare_reply(lreq); } -static void pam_handler_callback(void *pvt, int ldb_status, - struct ldb_result *res) +static void local_handler_callback(void *pvt, int ldb_status, + struct ldb_result *res) { struct LOCAL_request *lreq; const char *username = NULL; @@ -243,9 +272,11 @@ static void pam_handler_callback(void *pvt, int ldb_status, char *newauthtok = NULL; char *new_hash = NULL; char *authtok = NULL; + struct pam_data *pd; int ret; lreq = talloc_get_type(pvt, struct LOCAL_request); + pd = lreq->preq->pd; DEBUG(4, ("pam_handler_callback called with ldb_status [%d].\n", ldb_status)); @@ -256,8 +287,8 @@ static void pam_handler_callback(void *pvt, int ldb_status, if (res->count < 1) { DEBUG(4, ("No user found with filter ["SYSDB_PWNAM_FILTER"]\n", - lreq->pd->user)); - lreq->pd->pam_status = PAM_USER_UNKNOWN; + pd->user)); + pd->pam_status = PAM_USER_UNKNOWN; goto done; } else if (res->count > 1) { DEBUG(4, ("More than one object found with filter ["SYSDB_PWNAM_FILTER"]\n")); @@ -266,27 +297,26 @@ static void pam_handler_callback(void *pvt, int ldb_status, } username = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL); - if (strcmp(username, lreq->pd->user) != 0) { - DEBUG(1, ("Expected username [%s] get [%s].\n", lreq->pd->user, username)); + if (strcmp(username, pd->user) != 0) { + DEBUG(1, ("Expected username [%s] get [%s].\n", pd->user, username)); lreq->error = EINVAL; goto done; } lreq->res = res; - switch (lreq->pd->cmd) { + switch (pd->cmd) { case SSS_PAM_AUTHENTICATE: case SSS_PAM_CHAUTHTOK: - if (lreq->pd->cmd == SSS_PAM_CHAUTHTOK && lreq->cctx->priv == 1) { + if (pd->cmd == SSS_PAM_CHAUTHTOK && lreq->preq->cctx->priv == 1) { /* TODO: maybe this is a candiate for an explicit audit message. */ DEBUG(4, ("allowing root to reset a password.\n")); break; } - ret = authtok2str(lreq, lreq->pd->authtok, - lreq->pd->authtok_size, &authtok); + ret = authtok2str(lreq, pd->authtok, pd->authtok_size, &authtok); NEQ_CHECK_OR_JUMP(ret, EOK, ("authtok2str failed.\n"), lreq->error, ret, done); - memset(lreq->pd->authtok, 0, lreq->pd->authtok_size); + memset(pd->authtok, 0, pd->authtok_size); password = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_PWD, NULL); NULL_CHECK_OR_JUMP(password, ("No password stored.\n"), @@ -294,7 +324,7 @@ static void pam_handler_callback(void *pvt, int ldb_status, DEBUG(4, ("user: [%s], password hash: [%s]\n", username, password)); new_hash = nss_sha512_crypt(authtok, password); - memset(authtok, 0, lreq->pd->authtok_size); + memset(authtok, 0, pd->authtok_size); NULL_CHECK_OR_JUMP(new_hash, ("nss_sha512_crypt failed.\n"), lreq->error, EFAULT, done); @@ -309,7 +339,7 @@ static void pam_handler_callback(void *pvt, int ldb_status, break; } - switch (lreq->pd->cmd) { + switch (pd->cmd) { case SSS_PAM_AUTHENTICATE: do_successful_login(lreq); return; @@ -334,23 +364,22 @@ static void pam_handler_callback(void *pvt, int ldb_status, } done: - if (lreq->pd->authtok != NULL) - memset(lreq->pd->authtok, 0, lreq->pd->authtok_size); + if (pd->authtok != NULL) + memset(pd->authtok, 0, pd->authtok_size); if (authtok != NULL) - memset(authtok, 0, lreq->pd->authtok_size); - if (lreq->pd->newauthtok != NULL) - memset(lreq->pd->newauthtok, 0, lreq->pd->newauthtok_size); + memset(authtok, 0, pd->authtok_size); + if (pd->newauthtok != NULL) + memset(pd->newauthtok, 0, pd->newauthtok_size); if (newauthtok != NULL) - memset(newauthtok, 0, lreq->pd->newauthtok_size); + memset(newauthtok, 0, pd->newauthtok_size); prepare_reply(lreq); } -int LOCAL_pam_handler(struct cli_ctx *cctx, pam_dp_callback_t callback, - struct sss_domain_info *dom, struct pam_data *pd) +int LOCAL_pam_handler(struct pam_auth_req *preq) { int ret; - struct LOCAL_request *lreq=NULL; + struct LOCAL_request *lreq; static const char *attrs[] = {SYSDB_NAME, SYSDB_PWD, @@ -364,37 +393,27 @@ int LOCAL_pam_handler(struct cli_ctx *cctx, pam_dp_callback_t callback, "lastFailedLogin", NULL}; - lreq = talloc_zero(cctx, struct LOCAL_request); + DEBUG(4, ("LOCAL pam handler.\n")); + + lreq = talloc_zero(preq, struct LOCAL_request); if (!lreq) { return ENOMEM; } - lreq->cctx = cctx; - lreq->pd = pd; - lreq->callback = callback; - lreq->pd->pam_status = PAM_SUCCESS; - lreq->error = EOK; - - - DEBUG(4, ("LOCAL pam handler.\n")); - lreq->domain_info = dom; - NULL_CHECK_OR_JUMP(lreq->domain_info, ("Domain info not found.\n"), - ret, EINVAL, done); + lreq->dbctx = preq->cctx->rctx->sysdb; + lreq->preq = preq; - lreq->dbctx = lreq->cctx->rctx->sysdb; + preq->pd->pam_status = PAM_SUCCESS; ret = sysdb_get_user_attr(lreq, lreq->dbctx, - lreq->domain_info, lreq->pd->user, attrs, - pam_handler_callback, lreq); + preq->domain, preq->pd->user, attrs, + local_handler_callback, preq); - if(ret != EOK) { + if (ret != EOK) { DEBUG(1, ("sysdb_get_user_attr failed.\n")); - goto done; + talloc_free(lreq); + return ret; } return EOK; - -done: - talloc_free(lreq); - return ret; } diff --git a/server/responder/pam/pam_LOCAL_domain.h b/server/responder/pam/pam_LOCAL_domain.h deleted file mode 100644 index bc2064db..00000000 --- a/server/responder/pam/pam_LOCAL_domain.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __PAM_LOCAL_DOMAIN_H__ -#define __PAM_LOCAL_DOMAIN_H__ - -#include "responder/pam/pamsrv.h" - -int LOCAL_pam_handler(struct cli_ctx *cctx, pam_dp_callback_t callback, - struct sss_domain_info *dom, struct pam_data *pd); - -#endif /* __PAM_LOCAL_DOMAIN_H__ */ diff --git a/server/responder/pam/pamsrv.c b/server/responder/pam/pamsrv.c index 2f74a4ac..1adbb14c 100644 --- a/server/responder/pam/pamsrv.c +++ b/server/responder/pam/pamsrv.c @@ -3,7 +3,8 @@ PAM Responder - Copyright (C) Simo Sorce <ssorce@redhat.com> 2008 + Copyright (C) Simo Sorce <ssorce@redhat.com> 2009 + Copyright (C) Sumit Bose <sbose@redhat.com> 2009 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 diff --git a/server/responder/pam/pamsrv.h b/server/responder/pam/pamsrv.h index 077b495d..c751ceed 100644 --- a/server/responder/pam/pamsrv.h +++ b/server/responder/pam/pamsrv.h @@ -1,59 +1,34 @@ #ifndef __PAMSRV_H__ #define __PAMSRV_H__ - +#include <security/pam_appl.h> #include "util/util.h" #include "sbus/sssd_dbus.h" #include "responder/common/responder.h" #define PAM_DP_TIMEOUT 5000 -#define DEBUG_PAM_DATA(level, pd) do { \ - if (level <= debug_level) pam_print_data(level, pd); \ -} while(0); +struct pam_auth_req; -struct response_data { - int32_t type; - int32_t len; - uint8_t *data; - struct response_data *next; -}; +typedef void (pam_dp_callback_t)(struct pam_auth_req *preq); -struct pam_data { - int cmd; - uint32_t authtok_type; - uint32_t authtok_size; - uint32_t newauthtok_type; - uint32_t newauthtok_size; - char *domain; - char *user; - char *service; - char *tty; - char *ruser; - char *rhost; - uint8_t *authtok; - uint8_t *newauthtok; - - int pam_status; - int response_delay; - struct response_data *resp_list; +struct pam_auth_req { struct cli_ctx *cctx; -}; + struct sss_domain_info *domain; -int pam_add_response(struct pam_data *pd, enum response_type type, - int len, const uint8_t *data); -void pam_print_data(int l, struct pam_data *pd); + struct pam_data *pd; -typedef void (*pam_dp_callback_t)(struct pam_data *pd); + pam_dp_callback_t *callback; +}; struct sbus_method *register_pam_dp_methods(void); struct sss_cmd_table *register_sss_cmds(void); -int pam_dp_send_req(struct cli_ctx *cctx, pam_dp_callback_t callback, - int timeout, struct pam_data *pd); +int pam_dp_send_req(struct pam_auth_req *preq, int timeout); + +int pam_cache_credentials(struct pam_auth_req *preq); +int pam_cache_auth(struct pam_auth_req *preq); + +int LOCAL_pam_handler(struct pam_auth_req *preq); -bool dp_pack_pam_request(DBusMessage *msg, struct pam_data *pd); -bool dp_unpack_pam_request(DBusMessage *msg, struct pam_data *pd, DBusError *dbus_error); -bool dp_pack_pam_response(DBusMessage *msg, struct pam_data *pd); -bool dp_unpack_pam_response(DBusMessage *msg, struct pam_data *pd, DBusError *dbus_error); #endif /* __PAMSRV_H__ */ diff --git a/server/responder/pam/pamsrv_cache.c b/server/responder/pam/pamsrv_cache.c new file mode 100644 index 00000000..10f41996 --- /dev/null +++ b/server/responder/pam/pamsrv_cache.c @@ -0,0 +1,275 @@ +/* + SSSD + + PAM cache credentials + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2009 + Copyright (C) Sumit Bose <sbose@redhat.com> 2009 + + 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 <security/pam_modules.h> +#include <time.h> +#include "util/util.h" +#include "db/sysdb.h" +#include "util/nss_sha512crypt.h" +#include "providers/data_provider.h" +#include "responder/pam/pamsrv.h" + +static int authtok2str(const void *mem_ctx, + uint8_t *src, const int src_size, + char **_dest) +{ + char *dest; + + if ((src == NULL && src_size != 0) || + (src != NULL && *src != '\0' && src_size == 0)) { + return EINVAL; + } + + dest = talloc_size(mem_ctx, src_size + 1); + if (dest == NULL) { + return ENOMEM; + } + + memcpy(dest, src, src_size); + dest[src_size]='\0'; + + *_dest = dest; + return EOK; +} + +struct set_attrs_ctx { + struct pam_auth_req *preq; + struct sysdb_attrs *attrs; + struct sysdb_req *sysreq; +}; + +static void pc_set_user_attr_callback(void *pvt, + int ldb_status, + struct ldb_result *res) +{ + struct set_attrs_ctx *ctx; + int error; + + ctx = talloc_get_type(pvt, struct set_attrs_ctx); + error = sysdb_error_to_errno(ldb_status); + + sysdb_transaction_done(ctx->sysreq, error); + + if (ldb_status != LDB_SUCCESS) { + DEBUG(2, ("Failed to cache credentials for user [%s] (%d)!\n", + ctx->preq->pd->user, error, strerror(error))); + } + + ctx->preq->callback(ctx->preq); +} + +static void pc_set_user_attr_req(struct sysdb_req *req, void *pvt) +{ + struct set_attrs_ctx *ctx; + int ret; + + DEBUG(4, ("entering pc_set_user_attr_req\n")); + + ctx = talloc_get_type(pvt, struct set_attrs_ctx); + + ctx->sysreq = req; + + ret = sysdb_set_user_attr(req, ctx->preq->cctx->rctx->sysdb, + ctx->preq->domain, + ctx->preq->pd->user, + ctx->attrs, + pc_set_user_attr_callback, ctx); + if (ret != EOK) { + sysdb_transaction_done(ctx->sysreq, ret); + } + + if (ret != EOK) { + DEBUG(2, ("Failed to cache credentials for user [%s] (%d)!\n", + ctx->preq->pd->user, ret, strerror(ret))); + ctx->preq->callback(ctx->preq); + } +} + +int pam_cache_credentials(struct pam_auth_req *preq) +{ + struct set_attrs_ctx *ctx; + struct pam_data *pd; + char *password = NULL; + char *comphash = NULL; + char *salt; + int i, ret; + + pd = preq->pd; + + ret = authtok2str(preq, pd->authtok, pd->authtok_size, &password); + if (ret) { + DEBUG(4, ("Invalid auth token.\n")); + ret = EINVAL; + goto done; + } + + salt = gen_salt(); + if (!salt) { + DEBUG(4, ("Failed to generate random salt.\n")); + ret = EFAULT; + goto done; + } + + comphash = nss_sha512_crypt(password, salt); + if (!comphash) { + DEBUG(4, ("Failed to create password hash.\n")); + ret = EFAULT; + goto done; + } + + ctx = talloc_zero(preq, struct set_attrs_ctx); + if (!ctx) { + ret = ENOMEM; + goto done; + } + ctx->preq = preq; + + ctx->attrs = sysdb_new_attrs(ctx); + if (!ctx->attrs) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_attrs_add_string(ctx->attrs, SYSDB_PWD, comphash); + if (ret) goto done; + + /* FIXME: should we use a different attribute for chache passwords ?? */ + ret = sysdb_attrs_add_long(ctx->attrs, "lastPasswordChange", + (long)time(NULL)); + if (ret) goto done; + + ret = sysdb_transaction(ctx, preq->cctx->rctx->sysdb, + pc_set_user_attr_req, ctx); + +done: + if (password) for (i = 0; password[i]; i++) password[i] = 0; + if (ret != EOK) { + DEBUG(2, ("Failed to cache credentials for user [%s] (%d)!\n", + pd->user, ret, strerror(ret))); + } + return ret; +} + +static void pam_cache_auth_return(struct pam_auth_req *preq, int error) +{ + preq->pd->pam_status = error; + preq->callback(preq); +} + +static void pam_cache_auth_callback(void *pvt, int ldb_status, + struct ldb_result *res) +{ + struct pam_auth_req *preq; + struct pam_data *pd; + const char *userhash; + const char *comphash; + char *password = NULL; + int i, ret; + + preq = talloc_get_type(pvt, struct pam_auth_req); + pd = preq->pd; + + if (ldb_status != LDB_SUCCESS) { + DEBUG(4, ("User info retireval failed! (%d [%s])\n", + ldb_status, sysdb_error_to_errno(ldb_status))); + + ret = PAM_SYSTEM_ERR; + goto done; + } + + if (res->count == 0) { + DEBUG(4, ("User [%s@%s] not found.\n", + pd->user, preq->domain->name)); + ret = PAM_USER_UNKNOWN; + goto done; + } + + if (res->count != 1) { + DEBUG(4, ("Too manyt results for user [%s@%s].\n", + pd->user, preq->domain->name)); + ret = PAM_SYSTEM_ERR; + goto done; + } + + /* TODO: verify user account (failed logins, disabled, expired ...) */ + + ret = authtok2str(preq, pd->authtok, pd->authtok_size, &password); + if (ret) { + DEBUG(4, ("Invalid auth token.\n")); + ret = PAM_AUTH_ERR; + goto done; + } + + userhash = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_PWD, NULL); + if (userhash == NULL || *userhash == '\0') { + DEBUG(4, ("Cached credentials not available.\n")); + ret = PAM_AUTHINFO_UNAVAIL; + goto done; + } + + comphash = nss_sha512_crypt(password, userhash); + if (!comphash) { + DEBUG(4, ("Failed to create password hash.\n")); + ret = PAM_SYSTEM_ERR; + goto done; + } + + if (strcmp(userhash, comphash) == 0) { + /* TODO: probable good point for audit logging */ + DEBUG(4, ("Hashes do match!\n")); + ret = PAM_SUCCESS; + goto done; + } + + DEBUG(4, ("Authentication failed.\n")); + ret = PAM_AUTH_ERR; + +done: + if (password) for (i = 0; password[i]; i++) password[i] = 0; + pam_cache_auth_return(preq, ret); +} + +int pam_cache_auth(struct pam_auth_req *preq) +{ + int ret; + + static const char *attrs[] = {SYSDB_NAME, + SYSDB_PWD, + SYSDB_DISABLED, + SYSDB_LAST_LOGIN, + "lastPasswordChange", + "accountExpires", + "failedLoginAttempts", + "lastFailedLogin", + NULL}; + + ret = sysdb_get_user_attr(preq, preq->cctx->rctx->sysdb, + preq->domain, preq->pd->user, attrs, + pam_cache_auth_callback, preq); + + if (ret != EOK) { + DEBUG(2, ("sysdb_get_user_attr failed.\n")); + } + + return ret; +} + diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c index db5f064f..2190b566 100644 --- a/server/responder/pam/pamsrv_cmd.c +++ b/server/responder/pam/pamsrv_cmd.c @@ -1,10 +1,32 @@ +/* + SSSD + + PAM Responder + + Copyright (C) Simo Sorce <ssorce@redhat.com> 2009 + Copyright (C) Sumit Bose <sbose@redhat.com> 2009 + + 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 <talloc.h> #include "util/util.h" #include "confdb/confdb.h" #include "responder/common/responder_packet.h" -#include "responder/pam/pam_LOCAL_domain.h" +#include "providers/data_provider.h" #include "responder/pam/pamsrv.h" static int pam_parse_in_data(struct sss_names_ctx *snctx, @@ -86,19 +108,20 @@ static int pam_parse_in_data(struct sss_names_ctx *snctx, return EOK; } -static void pam_reply(struct pam_data *pd); +static void pam_reply(struct pam_auth_req *preq); static void pam_reply_delay(struct tevent_context *ev, struct tevent_timer *te, struct timeval tv, void *pvt) { - struct pam_data *pd; + struct pam_auth_req *preq; + DEBUG(4, ("pam_reply_delay get called.\n")); - pd = talloc_get_type(pvt, struct pam_data); + preq = talloc_get_type(pvt, struct pam_auth_req); - pam_reply(pd); + pam_reply(preq); } -static void pam_reply(struct pam_data *pd) +static void pam_reply(struct pam_auth_req *preq) { struct cli_ctx *cctx; uint8_t *body; @@ -111,13 +134,48 @@ static void pam_reply(struct pam_data *pd) int p; struct timeval tv; struct tevent_timer *te; + struct pam_data *pd; + + pd = preq->pd; DEBUG(4, ("pam_reply get called.\n")); + if ((pd->cmd == SSS_PAM_AUTHENTICATE) && + (preq->domain->cache_credentials == true) && + (pd->offline_auth == false)) { + + if (pd->pam_status == PAM_SUCCESS) { + pd->offline_auth = true; + preq->callback = pam_reply; + ret = pam_cache_credentials(preq); + if (ret == EOK) { + return; + } + else { + DEBUG(0, ("Failed to cache credentials")); + /* this error is not fatal, continue */ + } + } + + if (pd->pam_status == PAM_AUTHINFO_UNAVAIL) { + /* do auth with offline credentials */ + pd->offline_auth = true; + preq->callback = pam_reply; + ret = pam_cache_auth(preq); + if (ret == EOK) { + return; + } + else { + DEBUG(1, ("Failed to setup offline auth")); + /* this error is not fatal, continue */ + } + } + } + if (pd->response_delay > 0) { ret = gettimeofday(&tv, NULL); if (ret != EOK) { - DEBUG(0, ("gettimeofday failed [%d][%s].\n", + DEBUG(1, ("gettimeofday failed [%d][%s].\n", errno, strerror(errno))); err = ret; goto done; @@ -126,9 +184,9 @@ static void pam_reply(struct pam_data *pd) tv.tv_usec = 0; pd->response_delay = 0; - te = tevent_add_timer(cctx->ev, cctx, tv, pam_reply_delay, pd); + te = tevent_add_timer(cctx->ev, cctx, tv, pam_reply_delay, preq); if (te == NULL) { - DEBUG(0, ("Failed to add event pam_reply_delay.\n")); + DEBUG(1, ("Failed to add event pam_reply_delay.\n")); err = ENOMEM; goto done; } @@ -136,7 +194,7 @@ static void pam_reply(struct pam_data *pd) return; } - cctx = pd->cctx; + cctx = preq->cctx; ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in), &cctx->creq->out); @@ -191,8 +249,7 @@ static void pam_reply(struct pam_data *pd) } done: - talloc_free(pd); - sss_cmd_done(cctx, NULL); + sss_cmd_done(cctx, preq); } static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) @@ -201,50 +258,60 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) uint8_t *body; size_t blen; int ret; + struct pam_auth_req *preq; struct pam_data *pd; - pd = talloc(cctx, struct pam_data); - if (pd == NULL) return ENOMEM; + preq = talloc_zero(cctx, struct pam_auth_req); + if (!preq) { + return ENOMEM; + } + preq->cctx = cctx; + + preq->pd = talloc_zero(preq, struct pam_data); + if (!preq->pd) { + talloc_free(preq); + return ENOMEM; + } + pd = preq->pd; sss_packet_get_body(cctx->creq->in, &body, &blen); if (blen >= sizeof(uint32_t) && ((uint32_t *)(&body[blen - sizeof(uint32_t)]))[0] != END_OF_PAM_REQUEST) { DEBUG(1, ("Received data not terminated.\n")); - talloc_free(pd); + talloc_free(preq); return EINVAL; } pd->cmd = pam_cmd; - pd->cctx = cctx; - ret=pam_parse_in_data(cctx->rctx->names, pd, body, blen); - if( ret != 0 ) { - talloc_free(pd); + ret = pam_parse_in_data(cctx->rctx->names, pd, body, blen); + if (ret != 0) { + talloc_free(preq); return EINVAL; } - pd->response_delay = 0; - pd->resp_list = NULL; - if (pd->domain) { for (dom = cctx->rctx->domains; dom; dom = dom->next) { if (strcasecmp(dom->name, pd->domain) == 0) break; } if (!dom) { - talloc_free(pd); + talloc_free(preq); return EINVAL; } + preq->domain = dom; } else { DEBUG(4, ("Domain not provided, using default.\n")); - dom = cctx->rctx->domains; - pd->domain = dom->name; + preq->domain = cctx->rctx->domains; + pd->domain = preq->domain->name; } - if (!dom->provider) { - return LOCAL_pam_handler(cctx, pam_reply, dom, pd); + if (!preq->domain->provider) { + preq->callback = pam_reply; + return LOCAL_pam_handler(preq); }; - ret = pam_dp_send_req(cctx, pam_reply, PAM_DP_TIMEOUT, pd); + preq->callback = pam_reply; + ret = pam_dp_send_req(preq, PAM_DP_TIMEOUT); DEBUG(4, ("pam_dp_send_req returned %d\n", ret)); return ret; diff --git a/server/responder/pam/pamsrv_dp.c b/server/responder/pam/pamsrv_dp.c index 3555c20e..f352b270 100644 --- a/server/responder/pam/pamsrv_dp.c +++ b/server/responder/pam/pamsrv_dp.c @@ -32,21 +32,15 @@ #include "providers/dp_sbus.h" #include "responder/pam/pamsrv.h" -struct pam_reply_ctx { - struct cli_ctx *cctx; - struct pam_data *pd; - pam_dp_callback_t callback; -}; - -static void pam_process_dp_reply(DBusPendingCall *pending, void *ptr) +static void pam_dp_process_reply(DBusPendingCall *pending, void *ptr) { DBusError dbus_error; DBusMessage* msg; int ret; int type; - struct pam_reply_ctx *rctx; + struct pam_auth_req *preq; - rctx = talloc_get_type(ptr, struct pam_reply_ctx); + preq = talloc_get_type(ptr, struct pam_auth_req); dbus_error_init(&dbus_error); @@ -54,7 +48,7 @@ static void pam_process_dp_reply(DBusPendingCall *pending, void *ptr) msg = dbus_pending_call_steal_reply(pending); if (msg == NULL) { DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); - rctx->pd->pam_status = PAM_SYSTEM_ERR; + preq->pd->pam_status = PAM_SYSTEM_ERR; goto done; } @@ -62,57 +56,44 @@ static void pam_process_dp_reply(DBusPendingCall *pending, void *ptr) type = dbus_message_get_type(msg); switch (type) { case DBUS_MESSAGE_TYPE_METHOD_RETURN: - ret = dp_unpack_pam_response(msg, rctx->pd, &dbus_error); + ret = dp_unpack_pam_response(msg, preq->pd, &dbus_error); if (!ret) { DEBUG(0, ("Failed to parse reply.\n")); - rctx->pd->pam_status = PAM_SYSTEM_ERR; + preq->pd->pam_status = PAM_SYSTEM_ERR; goto done; } - DEBUG(4, ("received: [%d][%s]\n", rctx->pd->pam_status, rctx->pd->domain)); + DEBUG(4, ("received: [%d][%s]\n", preq->pd->pam_status, preq->pd->domain)); break; case DBUS_MESSAGE_TYPE_ERROR: DEBUG(0, ("Reply error.\n")); - rctx->pd->pam_status = PAM_SYSTEM_ERR; + preq->pd->pam_status = PAM_SYSTEM_ERR; break; default: DEBUG(0, ("Default... what now?.\n")); - rctx->pd->pam_status = PAM_SYSTEM_ERR; + preq->pd->pam_status = PAM_SYSTEM_ERR; } done: dbus_pending_call_unref(pending); dbus_message_unref(msg); - rctx->callback(rctx->pd); - - talloc_free(rctx); + preq->callback(preq); } -int pam_dp_send_req(struct cli_ctx *cctx, - pam_dp_callback_t callback, - int timeout, struct pam_data *pd) +int pam_dp_send_req(struct pam_auth_req *preq, int timeout) { + struct pam_data *pd = preq->pd; DBusMessage *msg; DBusPendingCall *pending_reply; DBusConnection *conn; dbus_bool_t ret; - struct pam_reply_ctx *rctx; - rctx = talloc(cctx, struct pam_reply_ctx); - if (rctx == NULL) { - DEBUG(0,("Out of memory?!\n")); - return ENOMEM; - } - rctx->cctx = cctx; - rctx->callback = callback; - rctx->pd = pd; - - if (pd->domain==NULL || - pd->user==NULL || - pd->service==NULL || - pd->tty==NULL || - pd->ruser==NULL || - pd->rhost==NULL ) { + if ((pd->domain == NULL) || + (pd->user == NULL) || + (pd->service == NULL) || + (pd->tty == NULL) || + (pd->ruser == NULL) || + (pd->rhost == NULL) ) { return EINVAL; } @@ -120,12 +101,12 @@ int pam_dp_send_req(struct cli_ctx *cctx, * in some pathological cases it may happen that nss starts up before * dp connection code is actually able to establish a connection. */ - if (!cctx->rctx->dp_ctx) { + if (!preq->cctx->rctx->dp_ctx) { DEBUG(1, ("The Data Provider connection is not available yet!" " This maybe a bug, it shouldn't happen!\n")); return EIO; } - conn = sbus_get_connection(cctx->rctx->dp_ctx->scon_ctx); + conn = sbus_get_connection(preq->cctx->rctx->dp_ctx->scon_ctx); msg = dbus_message_new_method_call(NULL, DP_CLI_PATH, @@ -158,8 +139,8 @@ int pam_dp_send_req(struct cli_ctx *cctx, return EIO; } - dbus_pending_call_set_notify(pending_reply, pam_process_dp_reply, rctx, - NULL); + dbus_pending_call_set_notify(pending_reply, + pam_dp_process_reply, preq, NULL); dbus_message_unref(msg); return EOK; diff --git a/server/server.mk b/server/server.mk index 9785f636..07848939 100644 --- a/server/server.mk +++ b/server/server.mk @@ -7,6 +7,7 @@ UTIL_OBJ = \ util/usertools.o \ monitor/monitor_sbus.o \ providers/dp_sbus.o \ + providers/dp_auth_util.o \ sbus/sssd_dbus_common.o \ sbus/sssd_dbus_connection.o \ sbus/sssd_dbus_server.o \ @@ -30,7 +31,7 @@ DP_OBJ = \ providers/data_provider.o DP_BE_OBJ = \ - providers/data_provider_be.o \ + providers/data_provider_be.o PROXY_BE_OBJ = \ providers/proxy.o @@ -62,16 +63,16 @@ SYSDB_TEST_OBJ = \ INFP_TEST_OBJ = \ tests/infopipe-tests.o -CRYPT_OBJ = util/nss_sha512crypt.o +CRYPT_OBJ = \ + util/nss_sha512crypt.o PAMSRV_OBJ = \ responder/pam/pamsrv.o \ responder/pam/pamsrv_cmd.o \ + responder/pam/pamsrv_cache.o \ responder/pam/pam_LOCAL_domain.o \ responder/pam/pamsrv_dp.o -PAMSRV_UTIL_OBJ = responder/pam/pamsrv_util.o - $(LDAP_BE_OBJ): CFLAGS += $(LDAP_CFLAGS) $(CRYPT_OBJ): CFLAGS += $(NSS_CFLAGS) @@ -103,14 +104,14 @@ sbin/sssd: $(SERVER_OBJ) $(UTIL_OBJ) sbin/sssd_nss: $(NSSSRV_OBJ) $(UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(CC) -o sbin/sssd_nss $(NSSSRV_OBJ) $(UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(LDFLAGS) $(LIBS) -sbin/sssd_pam: $(PAMSRV_OBJ) $(UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(PAMSRV_UTIL_OBJ) $(CRYPT_OBJ) - $(CC) -o sbin/sssd_pam $(PAMSRV_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(CRYPT_OBJ) $(LDFLAGS) $(LIBS) $(NSS_LIBS) +sbin/sssd_pam: $(PAMSRV_OBJ) $(UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(CRYPT_OBJ) + $(CC) -o sbin/sssd_pam $(PAMSRV_OBJ) $(UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(CRYPT_OBJ) $(LDFLAGS) $(LIBS) $(NSS_LIBS) -sbin/sssd_dp: $(DP_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) - $(CC) -o sbin/sssd_dp $(DP_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) $(LDFLAGS) $(LIBS) +sbin/sssd_dp: $(DP_OBJ) $(UTIL_OBJ) + $(CC) -o sbin/sssd_dp $(DP_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) sbin/sssd_be: $(DP_BE_OBJ) $(UTIL_OBJ) - $(CC) -Wl,-E -o sbin/sssd_be $(DP_BE_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) $(LDFLAGS) $(LIBS) $(PAM_LIBS) + $(CC) -Wl,-E -o sbin/sssd_be $(DP_BE_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) $(PAM_LIBS) sbin/sssd_info: $(INFOPIPE_OBJ) $(UTIL_OBJ) $(CC) -o sbin/sssd_info $(INFOPIPE_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) |