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

[Freeipa-devel] [PATCH] Cahce credentials as hashes



Add code in the pam responder to cache credentials on successful
authentication and use the stored credentials if the backend returns
that it can't fetch information (offline).

Tested with the proxt auth module and pam_ldap.

Seems to work. One issue is that it seems that pam_ldap doesn't take
well the fact that the server may disappear. If one successful
connection to the ldap server have been performed it seem like pam_ldap
will keep trying to use the same connection eventually returning a PAM
system error. If sssd is restarted when the ldap server is not available
pam_ldap will give up immediately any attempt to connect and cached
credentials are used instead.
This makes using pam_ldap less then ideal in real deployments, but it is
ok for testing of offline cached credentials capabilities.

Simo.

-- 
Simo Sorce * Red Hat, Inc * New York
>From a1077444975f8f1fa65e3a8cc75007e834e9ea07 Mon Sep 17 00:00:00 2001
From: Simo Sorce <ssorce redhat com>
Date: Sat, 11 Apr 2009 00:31:50 -0400
Subject: [PATCH] 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)
---
 server/examples/sssdproxylocal          |    6 +-
 server/examples/sssdproxytest           |    8 +-
 server/providers/data_provider.c        |    1 -
 server/providers/data_provider.h        |   47 ++++++++
 server/providers/dp_backend.h           |    1 -
 server/responder/pam/pam_LOCAL_domain.c |  153 ++++++++++++++-----------
 server/responder/pam/pam_LOCAL_domain.h |    9 --
 server/responder/pam/pamsrv.c           |    3 +-
 server/responder/pam/pamsrv.h           |   53 +++-------
 server/responder/pam/pamsrv_cmd.c       |  123 ++++++++++++++++-----
 server/responder/pam/pamsrv_dp.c        |   63 ++++-------
 server/responder/pam/pamsrv_util.c      |  188 -------------------------------
 server/server.mk                        |   19 ++--
 13 files changed, 279 insertions(+), 395 deletions(-)
 delete mode 100644 server/responder/pam/pam_LOCAL_domain.h
 delete mode 100644 server/responder/pam/pamsrv_util.c

diff --git a/server/examples/sssdproxylocal b/server/examples/sssdproxylocal
index 1bc47f8..063dbff 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 9c5cb4a..1421796 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 4614250..e8f190e 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 4b68a0b..2c828fa 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/providers/dp_backend.h b/server/providers/dp_backend.h
index da71e75..27f79eb 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 7ee84eb..df2803e 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 bc2064d..0000000
--- 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 2f74a4a..1adbb14 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 077b495..c751cee 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_cmd.c b/server/responder/pam/pamsrv_cmd.c
index db5f064..2190b56 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 3555c20..f352b27 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 ab9b733..0000000
--- 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;
-}
-
diff --git a/server/server.mk b/server/server.mk
index 9785f63..0784893 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)
-- 
1.6.0.6


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