summaryrefslogtreecommitdiffstats
path: root/server/responder/pam/pamsrv_cache.c
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/pamsrv_cache.c
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/pamsrv_cache.c')
-rw-r--r--server/responder/pam/pamsrv_cache.c275
1 files changed, 275 insertions, 0 deletions
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;
+}
+