[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Freeipa-devel] [PATCH] first version of LOCAL pam backend



Hi,

please find enclosed a first version of the pam backend for the LOCAL
domain.

- currently authenticate, chauthtok and acct_mgmt work
- so far only glibc compatible sha512 passwords are used
- NSS is used for sha512 and random number generation
- currently I use direct libldb calls to be able to test things, I will
change this when Simo's work on sysdb is done

bye,
Sumit
>From b7ab3ad8d15af337dac89ec54af4382d3740ebfa Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose redhat com>
Date: Fri, 27 Feb 2009 20:32:31 +0100
Subject: [PATCH] first version of LOCAL pam backend

---
 server/Makefile.in                      |    3 +
 server/configure.ac                     |    1 +
 server/db/sysdb.h                       |    1 +
 server/responder/common/responder_cmd.h |   22 ++
 server/responder/pam/pam_LOCAL_domain.c |  295 ++++++++++++++++++++++
 server/responder/pam/pam_LOCAL_domain.h |    9 +
 server/responder/pam/pamsrv.h           |    7 +
 server/responder/pam/pamsrv_cmd.c       |   23 ++-
 server/server.mk                        |   12 +-
 server/util/nss_sha512crypt.c           |  419 +++++++++++++++++++++++++++++++
 server/util/nss_sha512crypt.h           |    3 +
 sss_client/pam_sss.c                    |    6 +
 12 files changed, 795 insertions(+), 6 deletions(-)
 create mode 100644 server/responder/pam/pam_LOCAL_domain.c
 create mode 100644 server/responder/pam/pam_LOCAL_domain.h
 create mode 100644 server/util/nss_sha512crypt.c
 create mode 100644 server/util/nss_sha512crypt.h

diff --git a/server/Makefile.in b/server/Makefile.in
index 6b865e8..bd320e3 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -43,6 +43,9 @@ DBUS_CFLAGS = @DBUS_CFLAGS@
 CHECK_LIBS = @CHECK_LIBS@
 CHECK_CFLAGS = @CHECK_CFLAGS@
 
+NSS_LIBS = @NSS_LIBS@
+NSS_CFLAGS = @NSS_CFLAGS@
+
 PAM_LIBS = @PAM_LIBS@
 
 OPENLDAP_LIBS = @OPENLDAP_LIBS@
diff --git a/server/configure.ac b/server/configure.ac
index 8921c78..d59d6fe 100644
--- a/server/configure.ac
+++ b/server/configure.ac
@@ -56,6 +56,7 @@ m4_include(util/signal.m4)
 
 PKG_CHECK_MODULES([DBUS],[dbus-1])
 PKG_CHECK_MODULES([CHECK],[check])
+PKG_CHECK_MODULES([NSS],[nss])
 
 AC_SUBST(TESTS)
 AC_SUBST(EXTRA_OBJ)
diff --git a/server/db/sysdb.h b/server/db/sysdb.h
index a112d65..01df3c0 100644
--- a/server/db/sysdb.h
+++ b/server/db/sysdb.h
@@ -43,6 +43,7 @@
 #define SYSDB_PW_HOMEDIR "homeDirectory"
 #define SYSDB_PW_SHELL "loginShell"
 #define SYSDB_PW_MEMBEROF "memberOf"
+#define SYSDB_PW_DISABLED "disabled"
 
 #define SYSDB_GR_NAME "gid"
 #define SYSDB_GR_GIDNUM "gidNumber"
diff --git a/server/responder/common/responder_cmd.h b/server/responder/common/responder_cmd.h
index 98a4b34..15141a0 100644
--- a/server/responder/common/responder_cmd.h
+++ b/server/responder/common/responder_cmd.h
@@ -40,6 +40,28 @@ struct cli_request {
     struct sss_packet *out;
 };
 
+struct nss_ctx {
+    struct tevent_context *ev;
+    struct tevent_fd *lfde;
+    int lfd;
+    struct sysdb_ctx *sysdb;
+    struct confdb_ctx *cdb;
+    char *sock_name;
+    struct service_sbus_ctx *ss_ctx;
+    struct service_sbus_ctx *dp_ctx;
+    struct btreemap *domain_map;
+    char *default_domain;
+
+    int cache_timeout;
+
+    struct sbus_method *sss_sbus_methods;
+    struct sss_cmd_table *sss_cmds;
+    const char *sss_pipe_name;
+    const char *confdb_socket_path;
+    struct sbus_method *dp_methods;
+};
+
+
 struct cli_ctx {
     struct tevent_context *ev;
     struct nss_ctx *nctx;
diff --git a/server/responder/pam/pam_LOCAL_domain.c b/server/responder/pam/pam_LOCAL_domain.c
new file mode 100644
index 0000000..54b6830
--- /dev/null
+++ b/server/responder/pam/pam_LOCAL_domain.c
@@ -0,0 +1,295 @@
+#include <security/pam_modules.h>
+
+#include "util/util.h"
+#include "responder/pam/pamsrv.h"
+#include "db/sysdb.h"
+#include "util/nss_sha512crypt.h"
+
+
+#define NULL_CHECK_OR_JUMP(var, msg, ret, err, label) do { \
+    if (var == NULL) { \
+        DEBUG(1, (msg)); \
+        ret = (err); \
+        goto label; \
+    } \
+} while(0)
+
+#define NEQ_CHECK_OR_JUMP(var, val, msg, ret, err, label) do { \
+    if (var != (val)) { \
+        DEBUG(1, (msg)); \
+        ret = (err); \
+        goto label; \
+    } \
+} while(0)
+
+
+struct LOCAL_request {
+    struct cli_ctx *cctx;
+    struct pam_data *pd;
+    pam_dp_callback_t callback;
+
+};
+
+struct callback_ctx {
+    struct cli_ctx *cctx;
+    pam_dp_callback_t callback;
+    int pam_status;
+    char *domain;
+};
+
+static int authtok2str(const void *mem_ctx, uint8_t *src, const int src_size, 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';
+
+    return EOK;
+}
+
+static void LOCAL_call_callback(struct tevent_context *ev,
+                                struct tevent_timer *te,
+                                struct timeval tv, void *pvt) {
+
+    struct callback_ctx *callback_ctx;
+    struct cli_ctx *cctx;
+    pam_dp_callback_t callback;
+    int pam_status;
+    char *domain;
+
+    callback_ctx = talloc_get_type(pvt, struct callback_ctx);
+    cctx = callback_ctx->cctx;
+    callback = callback_ctx->callback;
+    pam_status = callback_ctx->pam_status;
+    domain = callback_ctx->domain;
+
+    talloc_free(callback_ctx);
+
+    callback(cctx, pam_status, domain);
+}
+
+static void LOCAL_pam_handler(struct tevent_context *ev,
+                              struct tevent_timer *te,
+                              struct timeval tv, void *pvt)
+{
+    int ret;
+    int pam_status = PAM_SUCCESS;
+    struct LOCAL_request *lreq;
+    struct sysdb_ctx *dbctx=NULL;
+    struct ldb_result *res=NULL;
+    struct ldb_context *ldb_ctx;
+    struct ldb_dn *user_base_dn=NULL;
+    struct ldb_message *msg;
+    const char *attrs[] = {SYSDB_PW_NAME, SYSDB_PW_PWD, SYSDB_PW_DISABLED,
+                           NULL};
+    char *authtok=NULL;
+    char *newauthtok=NULL;
+    const char *username=NULL;
+    const char *password=NULL;
+    char *new_hash=NULL;
+    const char *disabled=NULL;
+    char *salt=NULL;
+    struct callback_ctx *callback_ctx;
+    int callback_delay;
+
+    DEBUG(4, ("LOCAL pam handler.\n"));
+    lreq = talloc_get_type(pvt, struct LOCAL_request);
+
+    ret = sysdb_init(lreq, lreq->cctx->ev, lreq->cctx->nctx->cdb, NULL, &dbctx);
+    NEQ_CHECK_OR_JUMP(ret, EOK, ("Could not set up LOCAL db"),
+                      pam_status, PAM_SYSTEM_ERR, done);
+
+    ldb_ctx = sysdb_ctx_get_ldb(dbctx);
+
+/* FIXME: replace with proper sysdb calls */
+    user_base_dn = ldb_dn_new_fmt(lreq, ldb_ctx, SYSDB_TMPL_USER_BASE,
+                                  "LOCAL");
+    NULL_CHECK_OR_JUMP(user_base_dn, ("Could not set up user base dn"),
+                       pam_status, PAM_SYSTEM_ERR, done);
+
+    ret = ldb_search(ldb_ctx, lreq, &res, user_base_dn, LDB_SCOPE_SUBTREE,
+                     attrs, SYSDB_PWNAM_FILTER, lreq->pd->user);
+    NEQ_CHECK_OR_JUMP(ret, LDB_SUCCESS, ("ldb_search failed"),
+                      pam_status, PAM_SYSTEM_ERR, done);
+
+    if (res->count < 1) {
+        DEBUG(4, ("No user found with filter ["SYSDB_PWNAM_FILTER"]\n", lreq->pd->user));
+        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", lreq->pd->user));
+        pam_status = PAM_SYSTEM_ERR;
+        goto done;
+    }
+
+    username = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_PW_NAME, NULL);
+    if (strcmp(username, lreq->pd->user) != 0) {
+        DEBUG(1, ("Expected username [%s] get [%s].\n", lreq->pd->user, username));
+        pam_status = PAM_SYSTEM_ERR;
+        goto done;
+    }
+
+    switch (lreq->pd->cmd) {
+        case SSS_PAM_AUTHENTICATE:
+        case SSS_PAM_CHAUTHTOK:
+            ret = authtok2str(lreq, lreq->pd->authtok,
+                              lreq->pd->authtok_size, &authtok);
+            NEQ_CHECK_OR_JUMP(ret, EOK, ("authtok2str failed.\n"),
+                              pam_status, PAM_SYSTEM_ERR, done);
+
+            ret = authtok2str(lreq, lreq->pd->newauthtok,
+                              lreq->pd->newauthtok_size, &newauthtok);
+            NEQ_CHECK_OR_JUMP(ret, EOK, ("authtok2str failed.\n"),
+                              pam_status, PAM_SYSTEM_ERR, done);
+
+            password = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_PW_PWD, NULL);
+            NULL_CHECK_OR_JUMP(password, ("No password stored.\n"),
+                               pam_status, PAM_SYSTEM_ERR, done);
+            DEBUG(4, ("user: [%s], password hash: [%s]\n", username, password));
+
+            new_hash = nss_sha512_crypt(authtok, password);
+            if (strcmp(new_hash, password) != 0) {
+                DEBUG(1, ("Passwords do not match.\n"));
+                pam_status = PAM_AUTH_ERR;
+/* TODO: maybe add more inteligent delay calculation */
+                callback_delay = 3;
+                goto done;
+            }
+            pam_status = PAM_SUCCESS;
+            break;
+    }
+
+    switch (lreq->pd->cmd) {
+        case SSS_PAM_AUTHENTICATE:
+            /* already done */
+            break;
+        case SSS_PAM_CHAUTHTOK:
+            salt = gen_salt();
+            NULL_CHECK_OR_JUMP(salt, ("Salt generation failed.\n"),
+                               pam_status, PAM_SYSTEM_ERR, done);
+            DEBUG(4, ("Using salt [%s]\n", salt));
+
+            new_hash = nss_sha512_crypt(newauthtok, salt);
+            NULL_CHECK_OR_JUMP(new_hash, ("Hash generation failed.\n"),
+                               pam_status, PAM_SYSTEM_ERR, done);
+            DEBUG(4, ("New hash [%s]\n", new_hash));
+
+            msg = ldb_msg_new(lreq);
+            msg->dn = res->msgs[0]->dn;
+            NULL_CHECK_OR_JUMP(msg, ("ldb_msg_new failed.\n"),
+                               pam_status, PAM_SYSTEM_ERR, done);
+
+            ret = ldb_msg_add_empty(msg, SYSDB_PW_PWD, LDB_FLAG_MOD_REPLACE,
+                                    NULL);
+            NEQ_CHECK_OR_JUMP(ret, LDB_SUCCESS, ("ldb_msg_add_empty failed.\n"),
+                              pam_status, PAM_SYSTEM_ERR, done);
+
+            ret = ldb_msg_add_string(msg, SYSDB_PW_PWD, new_hash);
+            NEQ_CHECK_OR_JUMP(ret, LDB_SUCCESS, ("ldb_msg_add_string failed.\n"),
+                              pam_status, PAM_SYSTEM_ERR, done);
+
+            ret = ldb_modify(ldb_ctx, msg);
+            NEQ_CHECK_OR_JUMP(ret, LDB_SUCCESS, ("ldb_modify failed.\n"),
+                              pam_status, PAM_SYSTEM_ERR, done);
+
+            pam_status = PAM_SUCCESS;
+            break;
+        case SSS_PAM_ACCT_MGMT:
+            disabled = ldb_msg_find_attr_as_string(res->msgs[0],
+                                                   SYSDB_PW_DISABLED, NULL);
+            if (disabled == NULL ||
+                strncasecmp(disabled, "false",5)==0 ||
+                strncasecmp(disabled, "no",2)==0 ) {
+                pam_status = PAM_SUCCESS;
+            } else {
+                pam_status = PAM_PERM_DENIED;
+            }
+            break;
+        case SSS_PAM_SETCRED:
+            pam_status = PAM_SUCCESS;
+            break;
+        case SSS_PAM_OPEN_SESSION:
+            pam_status = PAM_SUCCESS;
+            break;
+        case SSS_PAM_CLOSE_SESSION:
+            pam_status = PAM_SUCCESS;
+            break;
+        default:
+            pam_status = PAM_SYSTEM_ERR;
+            DEBUG(1, ("Unknown PAM task [%d].\n"));
+    }
+
+done:
+    if (lreq->pd->authtok != NULL)
+        memset(lreq->pd->authtok, 0, lreq->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);
+    if (newauthtok != NULL)
+        memset(newauthtok, 0, lreq->pd->newauthtok_size);
+    talloc_free(res);
+    talloc_free(user_base_dn);
+    talloc_free(dbctx);
+
+    callback_ctx = talloc(lreq->cctx, struct callback_ctx);
+    if (callback_ctx == NULL) {
+        DEBUG(1, ("Cannot prepare callback data.\n"));
+        return;
+    }
+
+    callback_ctx->cctx = lreq->cctx;
+    callback_ctx->callback = lreq->callback;
+    callback_ctx->pam_status = pam_status;
+    callback_ctx->domain = "LOCAL";
+
+    talloc_free(lreq);
+
+    ret = gettimeofday(&tv, NULL);
+    if (ret != 0) {
+        DEBUG(1, ("gettimeofday failed, continuing.\n"));
+    }
+    tv.tv_sec += callback_delay;
+    tv.tv_usec = 0;
+
+    te = tevent_add_timer(ev, callback_ctx->cctx, tv, LOCAL_call_callback,
+                          callback_ctx);
+    if (te == NULL) {
+        DEBUG(1, ("Cannot add callback to event loop.\n"));
+        return;
+    }
+}
+
+int LOCAL_schedule_request(struct cli_ctx *cctx, pam_dp_callback_t callback,
+                           struct pam_data *pd)
+{
+    struct LOCAL_request *lreq;
+    struct tevent_timer *te;
+    struct timeval tv;
+
+    lreq = talloc(cctx, struct LOCAL_request);
+    if (!lreq) {
+        return ENOMEM;
+    }
+    lreq->cctx = cctx;
+    lreq->pd = pd;
+    lreq->callback = callback;
+
+    /* fire immediately */
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+
+    te = tevent_add_timer(cctx->ev, cctx, tv, LOCAL_pam_handler, lreq);
+    if (te == NULL) {
+        return EIO;
+    }
+
+    return EOK;
+}
diff --git a/server/responder/pam/pam_LOCAL_domain.h b/server/responder/pam/pam_LOCAL_domain.h
new file mode 100644
index 0000000..a0a7ec5
--- /dev/null
+++ b/server/responder/pam/pam_LOCAL_domain.h
@@ -0,0 +1,9 @@
+#ifndef __PAM_LOCAL_DOMAIN_H__
+#define __PAM_LOCAL_DOMAIN_H__
+
+#include "responder/pam/pamsrv.h"
+
+int LOCAL_schedule_request(struct cli_ctx *cctx, pam_dp_callback_t callback,
+                           struct pam_data *pd);
+
+#endif /* __PAM_LOCAL_DOMAIN_H__ */
diff --git a/server/responder/pam/pamsrv.h b/server/responder/pam/pamsrv.h
index bb0082a..81b1ba9 100644
--- a/server/responder/pam/pamsrv.h
+++ b/server/responder/pam/pamsrv.h
@@ -1,3 +1,8 @@
+#ifndef __PAMSRV_H__
+#define __PAMSRV_H__
+
+
+#include "util/util.h"
 #include "sbus/sssd_dbus.h"
 #include "responder/common/responder_cmd.h"
 
@@ -31,3 +36,5 @@ 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);
+
+#endif /* __PAMSRV_H__ */
diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c
index 4fdded3..6c51869 100644
--- a/server/responder/pam/pamsrv_cmd.c
+++ b/server/responder/pam/pamsrv_cmd.c
@@ -2,6 +2,8 @@
 #include <talloc.h>
 
 #include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/pam/pam_LOCAL_domain.h"
 #include "responder/common/responder_common.h"
 #include "responder/common/responder_cmd.h"
 #include "responder/common/responder_packet.h"
@@ -125,9 +127,10 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
     size_t blen;
     int ret;
     struct pam_data *pd;
+    char *default_domain;
 
     pd = talloc(cctx, struct pam_data);
-    if (pd == NULL) return ENOMEM; 
+    if (pd == NULL) return ENOMEM;
 
     sss_packet_get_body(cctx->creq->in, &body, &blen);
     if (blen >= sizeof(uint32_t) &&
@@ -144,10 +147,26 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
         return EINVAL;
     }
 
+    if (pd->domain == NULL) {
+        ret = confdb_get_string(cctx->nctx->cdb, cctx, "config/domains",
+                                "defaultDomain", "LOCAL", &default_domain);
+        if (ret != EOK) {
+            DEBUG(1, ("Failed to call confdb.\n"));
+            talloc_free(pd);
+            return ret;
+        }
+        pd->domain = default_domain;
+        DEBUG(4, ("Using default domain [%s].\n", pd->domain));
+    }
+
+    if ( strncasecmp(pd->domain,"LOCAL",5) == 0 ) {
+        return LOCAL_schedule_request(cctx, pam_reply, pd);
+    };
+
     ret=pam_dp_send_req(cctx, pam_reply, PAM_DP_TIMEOUT, pd);
     DEBUG(4, ("pam_dp_send_req returned %d\n", ret));
 
-    return ret; 
+    return ret;
 }
 
 static int pam_cmd_authenticate(struct cli_ctx *cctx) {
diff --git a/server/server.mk b/server/server.mk
index 77f805c..0bf82a8 100644
--- a/server/server.mk
+++ b/server/server.mk
@@ -61,14 +61,18 @@ SYSDB_TEST_OBJ = \
 INFP_TEST_OBJ = \
 	tests/infopipe-tests.o
 
+CRYPT_OBJ = util/nss_sha512crypt.o
+
 PAMSRV_OBJ = \
     responder/pam/pamsrv.o \
     responder/pam/pamsrv_cmd.o \
+    responder/pam/pam_LOCAL_domain.o \
     responder/pam/pamsrv_dp.o
 
 PAMSRV_UTIL_OBJ = responder/pam/pamsrv_util.o
 
-PAM = -lpam
+$(CRYPT_OBJ): CFLAGS += $(NSS_CFLAGS)
+
 
 sbin/sssd: $(SERVER_OBJ) $(UTIL_OBJ)
 	$(CC) -o sbin/sssd $(SERVER_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS)
@@ -76,14 +80,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)
-	$(CC) -o sbin/sssd_pam $(PAMSRV_OBJ) $(UTIL_OBJ) $(PAMSRV_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_dp: $(DP_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ)
 	$(CC) -o sbin/sssd_dp $(DP_OBJ) $(UTIL_OBJ) $(PAMSRV_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)
+	$(CC) -Wl,-E -o sbin/sssd_be $(DP_BE_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) $(LDFLAGS) $(LIBS) $(PAM_LIBS)
 
 sbin/sssd_info: $(INFOPIPE_OBJ) $(UTIL_OBJ)
 	$(CC) -o sbin/sssd_info $(INFOPIPE_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS)
diff --git a/server/util/nss_sha512crypt.c b/server/util/nss_sha512crypt.c
new file mode 100644
index 0000000..84cb61c
--- /dev/null
+++ b/server/util/nss_sha512crypt.c
@@ -0,0 +1,419 @@
+/* This file is based on the work of Ulrich Drepper
+ * (http://people.redhat.com/drepper/SHA-crypt.txt). I have replaced the
+ * included SHA512 implementation by calls to NSS
+ * (http://www.mozilla.org/projects/security/pki/nss/).
+ *
+ *  Sumit Bose <sbose redhat com>
+ */
+/* SHA512-based Unix crypt implementation.
+   Released into the Public Domain by Ulrich Drepper <drepper redhat com>.  */
+
+#define _GNU_SOURCE
+#include <endian.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <prinit.h>
+#include <nss.h>
+#include <sechash.h>
+#include <pk11func.h>
+
+
+static int nspr_nss_init_done = 0;
+
+/* according to
+ * http://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1234224
+ * PR_Init must be called, but at least for the HASH_* calls it seems to work
+ * quite well without. */
+static int nspr_nss_init(void)
+{
+  int ret;
+  PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+  ret = NSS_NoDB_Init(NULL);
+  if (ret != SECSuccess) {
+    return ret;
+  }
+  nspr_nss_init_done = 1;
+  return 0;
+}
+
+/* added for completness, so far not used */
+static int nspr_nss_cleanup(void)
+{
+  int ret;
+  ret=NSS_Shutdown();
+  if (ret != SECSuccess ) {
+    return ret;
+  }
+  PR_Cleanup();
+  nspr_nss_init_done = 0;
+  return 0;
+}
+
+/* Define our magic string to mark salt for SHA512 "encryption"
+   replacement.  */
+static const char sha512_salt_prefix[] = "$6$";
+
+/* Prefix for optional rounds specification.  */
+static const char sha512_rounds_prefix[] = "rounds=";
+
+/* Maximum salt string length.  */
+#define SALT_LEN_MAX 16
+/* Default number of rounds if not explicitly specified.  */
+#define ROUNDS_DEFAULT 5000
+/* Minimum number of rounds.  */
+#define ROUNDS_MIN 1000
+/* Maximum number of rounds.  */
+#define ROUNDS_MAX 999999999
+
+/* Table with characters for base64 transformation.  */
+static const char b64t[64] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+
+static char *
+sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
+{
+  unsigned char alt_result[64]
+    __attribute__ ((__aligned__ (__alignof__ (uint64_t))));
+  unsigned char temp_result[64]
+    __attribute__ ((__aligned__ (__alignof__ (uint64_t))));
+  HASHContext *ctx;
+  HASHContext *alt_ctx;
+  size_t salt_len;
+  size_t key_len;
+  size_t cnt;
+  char *cp;
+  char *copied_key = NULL;
+  char *copied_salt = NULL;
+  char *p_bytes;
+  char *s_bytes;
+  /* Default number of rounds.  */
+  size_t rounds = ROUNDS_DEFAULT;
+  bool rounds_custom = false;
+
+  int ret;
+  unsigned int part;
+
+  /* Find beginning of salt string.  The prefix should normally always
+     be present.  Just in case it is not.  */
+  if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0)
+    /* Skip salt prefix.  */
+    salt += sizeof (sha512_salt_prefix) - 1;
+
+  if (strncmp (salt, sha512_rounds_prefix, sizeof (sha512_rounds_prefix) - 1)
+      == 0)
+    {
+      const char *num = salt + sizeof (sha512_rounds_prefix) - 1;
+      char *endp;
+      unsigned long int srounds = strtoul (num, &endp, 10);
+      if (*endp == '$')
+    {
+      salt = endp + 1;
+      rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX));
+      rounds_custom = true;
+    }
+    }
+
+  salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX);
+  key_len = strlen (key);
+
+  if ((key - (char *) 0) % __alignof__ (uint64_t) != 0)
+    {
+      char *tmp = (char *) alloca (key_len + __alignof__ (uint64_t));
+      key = copied_key =
+    memcpy (tmp + __alignof__ (uint64_t)
+        - (tmp - (char *) 0) % __alignof__ (uint64_t),
+        key, key_len);
+    }
+
+  if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0)
+    {
+      char *tmp = (char *) alloca (salt_len + __alignof__ (uint64_t));
+      salt = copied_salt =
+    memcpy (tmp + __alignof__ (uint64_t)
+        - (tmp - (char *) 0) % __alignof__ (uint64_t),
+        salt, salt_len);
+    }
+
+
+  if (!nspr_nss_init_done) {
+    ret = nspr_nss_init();
+    if (ret != SECSuccess) return NULL;
+  }
+
+  ctx = HASH_Create(HASH_AlgSHA512);
+  if ( ctx == NULL ) {
+    return NULL;
+  }
+
+  alt_ctx = HASH_Create(HASH_AlgSHA512);
+  if ( alt_ctx == NULL ) {
+    return NULL;
+  }
+
+
+  /* Prepare for the real work.  */
+  HASH_Begin(ctx);
+
+  /* Add the key string.  */
+  HASH_Update(ctx, key, key_len);
+
+  /* The last part is the salt string.  This must be at most 16
+     characters and it ends at the first `$' character (for
+     compatibility with existing implementations).  */
+  HASH_Update(ctx, salt, salt_len);
+
+
+  /* Compute alternate SHA512 sum with input KEY, SALT, and KEY.  The
+     final result will be added to the first context.  */
+  HASH_Begin(alt_ctx);
+
+  /* Add key.  */
+  HASH_Update(alt_ctx, key, key_len);
+
+  /* Add salt.  */
+  HASH_Update(alt_ctx, salt, salt_len);
+
+  /* Add key again.  */
+  HASH_Update(alt_ctx, key, key_len);
+
+  /* Now get result of this (64 bytes) and add it to the other
+     context.  */
+  HASH_End(alt_ctx, alt_result, &part, HASH_ResultLenContext(alt_ctx));
+
+  /* Add for any character in the key one byte of the alternate sum.  */
+  for (cnt = key_len; cnt > 64; cnt -= 64) {
+    HASH_Update(ctx, alt_result, 64);
+  }
+  HASH_Update(ctx, alt_result, cnt);
+
+  /* Take the binary representation of the length of the key and for every
+     1 add the alternate sum, for every 0 the key.  */
+  for (cnt = key_len; cnt > 0; cnt >>= 1)
+    if ((cnt & 1) != 0) {
+      HASH_Update(ctx, alt_result, 64);
+    } else {
+      HASH_Update(ctx, key, key_len);
+    }
+
+  /* Create intermediate result.  */
+  HASH_End(ctx, alt_result, &part, HASH_ResultLenContext(ctx));
+
+  /* Start computation of P byte sequence.  */
+  HASH_Begin(alt_ctx);
+
+  /* For every character in the password add the entire password.  */
+  for (cnt = 0; cnt < key_len; ++cnt) {
+    HASH_Update(alt_ctx, key, key_len);
+  }
+
+  /* Finish the digest.  */
+  HASH_End(alt_ctx, temp_result, &part, HASH_ResultLenContext(alt_ctx));
+
+  /* Create byte sequence P.  */
+  cp = p_bytes = alloca (key_len);
+  for (cnt = key_len; cnt >= 64; cnt -= 64)
+    cp = mempcpy (cp, temp_result, 64);
+  memcpy (cp, temp_result, cnt);
+
+  /* Start computation of S byte sequence.  */
+  HASH_Begin(alt_ctx);
+
+  /* For every character in the password add the entire password.  */
+  for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) {
+    HASH_Update(alt_ctx, salt, salt_len);
+  }
+
+  /* Finish the digest.  */
+  HASH_End(alt_ctx, temp_result, &part, HASH_ResultLenContext(alt_ctx));
+
+  /* Create byte sequence S.  */
+  cp = s_bytes = alloca (salt_len);
+  for (cnt = salt_len; cnt >= 64; cnt -= 64)
+    cp = mempcpy (cp, temp_result, 64);
+  memcpy (cp, temp_result, cnt);
+
+  /* Repeatedly run the collected hash value through SHA512 to burn
+     CPU cycles.  */
+  for (cnt = 0; cnt < rounds; ++cnt)
+    {
+      /* New context.  */
+      HASH_Begin(ctx);
+
+      /* Add key or last result.  */
+      if ((cnt & 1) != 0) {
+        HASH_Update(ctx, p_bytes, key_len);
+      } else {
+        HASH_Update(ctx, alt_result, 64);
+      }
+
+      /* Add salt for numbers not divisible by 3.  */
+      if (cnt % 3 != 0) {
+        HASH_Update(ctx, s_bytes, salt_len);
+      }
+
+      /* Add key for numbers not divisible by 7.  */
+      if (cnt % 7 != 0) {
+        HASH_Update(ctx, p_bytes, key_len);
+      }
+
+      /* Add key or last result.  */
+      if ((cnt & 1) != 0) {
+        HASH_Update(ctx, alt_result, 64);
+      } else {
+        HASH_Update(ctx, p_bytes, key_len);
+      }
+
+      /* Create intermediate result.  */
+      HASH_End(ctx, alt_result, &part, HASH_ResultLenContext(ctx));
+    }
+
+  /* Now we can construct the result string.  It consists of three
+     parts.  */
+  cp = __stpncpy (buffer, sha512_salt_prefix, MAX (0, buflen));
+  buflen -= sizeof (sha512_salt_prefix) - 1;
+
+  if (rounds_custom)
+    {
+      int n = snprintf (cp, MAX (0, buflen), "%s%zu$",
+            sha512_rounds_prefix, rounds);
+      cp += n;
+      buflen -= n;
+    }
+
+  cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len));
+  buflen -= MIN ((size_t) MAX (0, buflen), salt_len);
+
+  if (buflen > 0)
+    {
+      *cp++ = '$';
+      --buflen;
+    }
+
+#define b64_from_24bit(B2, B1, B0, N)                         \
+  do {                                        \
+    unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0);               \
+    int n = (N);                                  \
+    while (n-- > 0 && buflen > 0)                         \
+      {                                       \
+    *cp++ = b64t[w & 0x3f];                           \
+    --buflen;                                 \
+    w >>= 6;                                  \
+      }                                       \
+  } while (0)
+
+  b64_from_24bit (alt_result[0], alt_result[21], alt_result[42], 4);
+  b64_from_24bit (alt_result[22], alt_result[43], alt_result[1], 4);
+  b64_from_24bit (alt_result[44], alt_result[2], alt_result[23], 4);
+  b64_from_24bit (alt_result[3], alt_result[24], alt_result[45], 4);
+  b64_from_24bit (alt_result[25], alt_result[46], alt_result[4], 4);
+  b64_from_24bit (alt_result[47], alt_result[5], alt_result[26], 4);
+  b64_from_24bit (alt_result[6], alt_result[27], alt_result[48], 4);
+  b64_from_24bit (alt_result[28], alt_result[49], alt_result[7], 4);
+  b64_from_24bit (alt_result[50], alt_result[8], alt_result[29], 4);
+  b64_from_24bit (alt_result[9], alt_result[30], alt_result[51], 4);
+  b64_from_24bit (alt_result[31], alt_result[52], alt_result[10], 4);
+  b64_from_24bit (alt_result[53], alt_result[11], alt_result[32], 4);
+  b64_from_24bit (alt_result[12], alt_result[33], alt_result[54], 4);
+  b64_from_24bit (alt_result[34], alt_result[55], alt_result[13], 4);
+  b64_from_24bit (alt_result[56], alt_result[14], alt_result[35], 4);
+  b64_from_24bit (alt_result[15], alt_result[36], alt_result[57], 4);
+  b64_from_24bit (alt_result[37], alt_result[58], alt_result[16], 4);
+  b64_from_24bit (alt_result[59], alt_result[17], alt_result[38], 4);
+  b64_from_24bit (alt_result[18], alt_result[39], alt_result[60], 4);
+  b64_from_24bit (alt_result[40], alt_result[61], alt_result[19], 4);
+  b64_from_24bit (alt_result[62], alt_result[20], alt_result[41], 4);
+  b64_from_24bit (0, 0, alt_result[63], 2);
+
+  if (buflen <= 0)
+    {
+      errno = ERANGE;
+      buffer = NULL;
+    }
+  else
+    *cp = '\0';     /* Terminate the string.  */
+
+  /* Clear the buffer for the intermediate result so that people
+     attaching to processes or reading core dumps cannot get any
+     information.  We do it in this way to clear correct_words[]
+     inside the SHA512 implementation as well.  */
+  HASH_Destroy(ctx);
+  HASH_Destroy(alt_ctx);
+
+  memset (temp_result, '\0', sizeof (temp_result));
+  memset (p_bytes, '\0', key_len);
+  memset (s_bytes, '\0', salt_len);
+  memset (&ctx, '\0', sizeof (ctx));
+  memset (&alt_ctx, '\0', sizeof (alt_ctx));
+  if (copied_key != NULL)
+    memset (copied_key, '\0', key_len);
+  if (copied_salt != NULL)
+    memset (copied_salt, '\0', salt_len);
+
+  return buffer;
+}
+
+
+/* This entry point is equivalent to the `crypt' function in Unix
+   libcs.  */
+char *
+nss_sha512_crypt (const char *key, const char *salt)
+{
+  /* We don't want to have an arbitrary limit in the size of the
+     password.  We can compute an upper bound for the size of the
+     result in advance and so we can prepare the buffer we pass to
+     `sha512_crypt_r'.  */
+  static char *buffer;
+  static int buflen;
+  int needed = (sizeof (sha512_salt_prefix) - 1
+        + sizeof (sha512_rounds_prefix) + 9 + 1
+        + strlen (salt) + 1 + 86 + 1);
+
+  if (buflen < needed)
+    {
+      char *new_buffer = (char *) realloc (buffer, needed);
+      if (new_buffer == NULL)
+    return NULL;
+
+      buffer = new_buffer;
+      buflen = needed;
+    }
+
+  return sha512_crypt_r (key, salt, buffer, buflen);
+}
+
+char *gen_salt(void)
+{
+  int ret;
+  unsigned char bin_rand[12];
+  static char b64_rand[17];
+  char *cp;
+  int buflen;
+
+  if (!nspr_nss_init_done) {
+    ret = nspr_nss_init();
+    if (ret != SECSuccess) return NULL;
+  }
+
+  ret = PK11_GenerateRandom(bin_rand, sizeof(bin_rand)-1);
+  cp = b64_rand;
+  buflen = 16;
+  b64_from_24bit (bin_rand[0], bin_rand[1], bin_rand[2], 4);
+  b64_from_24bit (bin_rand[3], bin_rand[4], bin_rand[5], 4);
+  b64_from_24bit (bin_rand[6], bin_rand[7], bin_rand[8], 4);
+  b64_from_24bit (bin_rand[9], bin_rand[10], bin_rand[11], 4);
+
+  *cp++ = '\0';
+
+  return b64_rand;
+
+}
+
diff --git a/server/util/nss_sha512crypt.h b/server/util/nss_sha512crypt.h
new file mode 100644
index 0000000..0d06321
--- /dev/null
+++ b/server/util/nss_sha512crypt.h
@@ -0,0 +1,3 @@
+
+char * nss_sha512_crypt (const char *key, const char *salt);
+char *gen_salt(void);
diff --git a/sss_client/pam_sss.c b/sss_client/pam_sss.c
index 25a1d2c..bfb0ad1 100644
--- a/sss_client/pam_sss.c
+++ b/sss_client/pam_sss.c
@@ -102,6 +102,12 @@ static int pam_sss(int task, pam_handle_t *pamh, int flags, int argc,
 
     D(("Hello pam_sssd: %d", task));
 
+/* TODO: add useful prelim check */
+    if (task == SSS_PAM_CHAUTHTOK && (flags & PAM_PRELIM_CHECK)) {
+        D(("ignoring PAM_PRELIM_CHECK"));
+        return PAM_SUCCESS;
+    }
+
     ret = get_pam_items(pamh, &pi);
     if (ret != PAM_SUCCESS) {
         D(("get items returned error: %s", pam_strerror(pamh,ret)));
-- 
1.6.0.6


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]