summaryrefslogtreecommitdiffstats
path: root/server/responder/pam
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2009-04-11 00:31:50 -0400
committerSimo Sorce <ssorce@redhat.com>2009-04-13 09:07:10 -0400
commit943df8483b9f8a43df72121883ca67f17571d214 (patch)
treea9301004163a7033b4a342057c355da1ea49454b /server/responder/pam
parentf16705ecade500f77b525d1a3df0109196c98ee0 (diff)
downloadsssd-943df8483b9f8a43df72121883ca67f17571d214.tar.gz
sssd-943df8483b9f8a43df72121883ca67f17571d214.tar.xz
sssd-943df8483b9f8a43df72121883ca67f17571d214.zip
Implement credentials caching in pam responder.
Implement credentials caching in pam responder. Currently works only for the proxy backend. Also cleanup pam responder code and mode common code in data provider. (the data provider should never include responder private headers)
Diffstat (limited to 'server/responder/pam')
-rw-r--r--server/responder/pam/pam_LOCAL_domain.c153
-rw-r--r--server/responder/pam/pam_LOCAL_domain.h9
-rw-r--r--server/responder/pam/pamsrv.c3
-rw-r--r--server/responder/pam/pamsrv.h53
-rw-r--r--server/responder/pam/pamsrv_cache.c275
-rw-r--r--server/responder/pam/pamsrv_cmd.c123
-rw-r--r--server/responder/pam/pamsrv_dp.c63
-rw-r--r--server/responder/pam/pamsrv_util.c188
8 files changed, 494 insertions, 373 deletions
diff --git a/server/responder/pam/pam_LOCAL_domain.c b/server/responder/pam/pam_LOCAL_domain.c
index 7ee84eb6a..df2803e5b 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 bc2064dbb..000000000
--- 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 2f74a4ac3..1adbb14c1 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 077b495d4..c751ceed5 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 000000000..10f419967
--- /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 db5f064fd..2190b5668 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 3555c20ec..f352b270f 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/responder/pam/pamsrv_util.c b/server/responder/pam/pamsrv_util.c
deleted file mode 100644
index ab9b733e5..000000000
--- a/server/responder/pam/pamsrv_util.c
+++ /dev/null
@@ -1,188 +0,0 @@
-#include "util/util.h"
-#include "responder/pam/pamsrv.h"
-
-void pam_print_data(int l, struct pam_data *pd)
-{
- DEBUG(l, ("command: %d\n", pd->cmd));
- DEBUG(l, ("domain: %s\n", pd->domain));
- DEBUG(l, ("user: %s\n", pd->user));
- DEBUG(l, ("service: %s\n", pd->service));
- DEBUG(l, ("tty: %s\n", pd->tty));
- DEBUG(l, ("ruser: %s\n", pd->ruser));
- DEBUG(l, ("rhost: %s\n", pd->rhost));
- DEBUG(l, ("authtok type: %d\n", pd->authtok_type));
- DEBUG(l, ("authtok size: %d\n", pd->authtok_size));
- DEBUG(l, ("newauthtok type: %d\n", pd->newauthtok_type));
- DEBUG(l, ("newauthtok size: %d\n", pd->newauthtok_size));
-}
-
-int pam_add_response(struct pam_data *pd, enum response_type type,
- int len, const uint8_t *data)
-{
- struct response_data *new;
-
- new = talloc(pd, struct response_data);
- if (new == NULL) return ENOMEM;
-
- new->type = type;
- new->len = len;
- new->data = talloc_memdup(pd, data, len);
- if (new->data == NULL) return ENOMEM;
- new->next = pd->resp_list;
- pd->resp_list = new;
-
- return EOK;
-}
-
-bool dp_pack_pam_request(DBusMessage *msg, struct pam_data *pd)
-{
- int ret;
-
- 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);
-
- return ret;
-}
-
-bool dp_unpack_pam_request(DBusMessage *msg, struct pam_data *pd, DBusError *dbus_error)
-{
- int ret;
-
- ret = dbus_message_get_args(msg, 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);
-
- return ret;
-}
-
-bool dp_pack_pam_response(DBusMessage *msg, struct pam_data *pd)
-{
- int ret;
- struct response_data *resp;
-
- ret = dbus_message_append_args(msg,
- DBUS_TYPE_UINT32, &(pd->pam_status),
- DBUS_TYPE_STRING, &(pd->domain),
- DBUS_TYPE_INVALID);
- if (!ret) return ret;
-
- resp = pd->resp_list;
- while (resp != NULL) {
- ret=dbus_message_append_args(msg,
- DBUS_TYPE_UINT32, &(resp->type),
- DBUS_TYPE_UINT32, &(resp->len),
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
- &(resp->data),
- resp->len,
- DBUS_TYPE_INVALID);
- if (!ret) return ret;
-
- resp = resp->next;
- }
-
- return true;
-}
-
-bool dp_unpack_pam_response(DBusMessage *msg, struct pam_data *pd, DBusError *dbus_error)
-{
- DBusMessageIter iter;
- DBusMessageIter sub_iter;
- int type;
- int len;
- int len_msg;
- const uint8_t *data;
-
- if (!dbus_message_iter_init(msg, &iter)) {
- DEBUG(1, ("pam response has no arguments.\n"));
- return false;
- }
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) {
- DEBUG(1, ("pam response format error.\n"));
- return false;
- }
- dbus_message_iter_get_basic(&iter, &(pd->pam_status));
-
- if (!dbus_message_iter_next(&iter)) {
- DEBUG(1, ("pam response has too few arguments.\n"));
- return false;
- }
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
- DEBUG(1, ("pam response format error.\n"));
- return false;
- }
- dbus_message_iter_get_basic(&iter, &(pd->domain));
-
- while(dbus_message_iter_next(&iter)) {
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) {
- DEBUG(1, ("pam response format error.\n"));
- return false;
- }
- dbus_message_iter_get_basic(&iter, &type);
-
- if (!dbus_message_iter_next(&iter)) {
- DEBUG(1, ("pam response format error.\n"));
- return false;
- }
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) {
- DEBUG(1, ("pam response format error.\n"));
- return false;
- }
- dbus_message_iter_get_basic(&iter, &len);
-
- if (!dbus_message_iter_next(&iter)) {
- DEBUG(1, ("pam response format error.\n"));
- return false;
- }
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE) {
- DEBUG(1, ("pam response format error.\n"));
- return false;
- }
-
- dbus_message_iter_recurse(&iter, &sub_iter);
- dbus_message_iter_get_fixed_array(&sub_iter, &data, &len_msg);
- if (len != len_msg) {
- DEBUG(1, ("pam response format error.\n"));
- return false;
- }
-
- pam_add_response(pd, type, len, data);
-
- }
-
- return true;
-}
-