summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/responder/common/responder.h12
-rw-r--r--server/responder/common/responder_dp.c262
-rw-r--r--server/responder/nss/nsssrv.h17
-rw-r--r--server/responder/nss/nsssrv_dp.c261
-rw-r--r--server/responder/pam/pamsrv.h3
-rw-r--r--server/responder/pam/pamsrv_cmd.c335
6 files changed, 593 insertions, 297 deletions
diff --git a/server/responder/common/responder.h b/server/responder/common/responder.h
index 6f737a14..f5d5246f 100644
--- a/server/responder/common/responder.h
+++ b/server/responder/common/responder.h
@@ -115,4 +115,16 @@ int sss_cmd_get_version(struct cli_ctx *cctx);
/* responder_dp.c */
int sss_dp_init(struct resp_ctx *rctx, struct sbus_method dp_methods[]);
+#define NSS_DP_USER 1
+#define NSS_DP_GROUP 2
+#define NSS_DP_INITGROUPS 3
+
+typedef void (*nss_dp_callback_t)(uint16_t err_maj, uint32_t err_min,
+ const char *err_msg, void *ptr);
+
+int nss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *memctx,
+ nss_dp_callback_t callback, void *callback_ctx,
+ int timeout, const char *domain, int type,
+ const char *opt_name, uint32_t opt_id);
+
#endif /* __SSS_RESPONDER_H__ */
diff --git a/server/responder/common/responder_dp.c b/server/responder/common/responder_dp.c
index 7fc7e0b3..83f89a54 100644
--- a/server/responder/common/responder_dp.c
+++ b/server/responder/common/responder_dp.c
@@ -120,3 +120,265 @@ int sss_dp_init(struct resp_ctx *rctx, struct sbus_method *dp_methods)
return EOK;
}
+
+struct nss_dp_req {
+ nss_dp_callback_t callback;
+ void *callback_ctx;
+ struct tevent_timer *te;
+ DBusPendingCall *pending_reply;
+};
+
+static int nss_dp_req_destructor(void *ptr)
+{
+ struct nss_dp_req *req = talloc_get_type(ptr, struct nss_dp_req);
+
+ if (req->pending_reply) {
+ dbus_pending_call_cancel(req->pending_reply);
+ }
+
+ return 0;
+}
+
+static void nss_dp_send_acct_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *data)
+{
+ struct nss_dp_req *ndp_req;
+ dbus_uint16_t err_maj = DP_ERR_TIMEOUT;
+ dbus_uint32_t err_min = EIO;
+ const char *err_msg = "Request timed out";
+
+ ndp_req = talloc_get_type(data, struct nss_dp_req);
+
+ ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx);
+
+ talloc_free(ndp_req);
+}
+
+static int nss_dp_get_reply(DBusPendingCall *pending,
+ dbus_uint16_t *err_maj,
+ dbus_uint32_t *err_min,
+ const char **err_msg);
+
+static void nss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr)
+{
+ struct nss_dp_req *ndp_req;
+ dbus_uint16_t err_maj;
+ dbus_uint32_t err_min;
+ const char *err_msg;
+ int ret;
+
+ ndp_req = talloc_get_type(ptr, struct nss_dp_req);
+
+ /* free timeout event and remove request destructor */
+ talloc_free(ndp_req->te);
+ talloc_set_destructor(ndp_req, NULL);
+
+ ret = nss_dp_get_reply(pending, &err_maj, &err_min, &err_msg);
+ if (ret != EOK) {
+ err_maj = DP_ERR_FATAL;
+ err_min = ret;
+ err_msg = "Failed to get reply from Data Provider";
+ }
+
+ ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx);
+
+ talloc_free(ndp_req);
+}
+
+int nss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *memctx,
+ nss_dp_callback_t callback, void *callback_ctx,
+ int timeout, const char *domain, int type,
+ const char *opt_name, uint32_t opt_id)
+{
+ struct nss_dp_req *ndp_req;
+ DBusMessage *msg;
+ DBusPendingCall *pending_reply;
+ DBusConnection *conn;
+ dbus_bool_t ret;
+ uint32_t be_type;
+ const char *attrs = "core";
+ char *filter;
+ struct timeval tv;
+
+ /* either, or, not both */
+ if (opt_name && opt_id) {
+ return EINVAL;
+ }
+
+ if (!domain) {
+ return EINVAL;
+ }
+
+ switch (type) {
+ case NSS_DP_USER:
+ be_type = BE_REQ_USER;
+ break;
+ case NSS_DP_GROUP:
+ be_type = BE_REQ_GROUP;
+ break;
+ case NSS_DP_INITGROUPS:
+ be_type = BE_REQ_INITGROUPS;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (opt_name) {
+ filter = talloc_asprintf(memctx, "name=%s", opt_name);
+ } else if (opt_id) {
+ filter = talloc_asprintf(memctx, "idnumber=%u", opt_id);
+ } else {
+ filter = talloc_strdup(memctx, "name=*");
+ }
+ if (!filter) {
+ return ENOMEM;
+ }
+
+ /* double check dp_ctx has actually been initialized.
+ * in some pathological cases it may happen that nss starts up before
+ * dp connection code is actually able to establish a connection.
+ */
+ if (!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(rctx->dp_ctx->scon_ctx);
+
+ /* create the message */
+ msg = dbus_message_new_method_call(NULL,
+ DP_CLI_PATH,
+ DP_CLI_INTERFACE,
+ DP_SRV_METHOD_GETACCTINFO);
+ if (msg == NULL) {
+ DEBUG(0,("Out of memory?!\n"));
+ return ENOMEM;
+ }
+
+ DEBUG(4, ("Sending request for [%s][%u][%s][%s]\n",
+ domain, be_type, attrs, filter));
+
+ ret = dbus_message_append_args(msg,
+ DBUS_TYPE_STRING, &domain,
+ DBUS_TYPE_UINT32, &be_type,
+ DBUS_TYPE_STRING, &attrs,
+ DBUS_TYPE_STRING, &filter,
+ DBUS_TYPE_INVALID);
+ if (!ret) {
+ DEBUG(1,("Failed to build message\n"));
+ return EIO;
+ }
+
+ ret = dbus_connection_send_with_reply(conn, msg, &pending_reply,
+ 600000 /* TODO: set timeout */);
+ if (!ret) {
+ /*
+ * Critical Failure
+ * We can't communicate on this connection
+ * We'll drop it using the default destructor.
+ */
+ DEBUG(0, ("D-BUS send failed.\n"));
+ dbus_message_unref(msg);
+ return EIO;
+ }
+
+ ndp_req = talloc_zero(memctx, struct nss_dp_req);
+ if (!ndp_req) {
+ dbus_message_unref(msg);
+ return ENOMEM;
+ }
+ ndp_req->callback = callback;
+ ndp_req->callback_ctx = callback_ctx;
+
+ /* set up destructor */
+ ndp_req->pending_reply = pending_reply;
+ talloc_set_destructor((TALLOC_CTX *)ndp_req, nss_dp_req_destructor);
+
+ /* setup the timeout handler */
+ gettimeofday(&tv, NULL);
+ tv.tv_sec += timeout/1000;
+ tv.tv_usec += (timeout%1000) * 1000;
+ ndp_req->te = tevent_add_timer(rctx->ev, memctx, tv,
+ nss_dp_send_acct_timeout, ndp_req);
+
+ /* Set up the reply handler */
+ dbus_pending_call_set_notify(pending_reply,
+ nss_dp_send_acct_callback,
+ ndp_req, NULL);
+ dbus_message_unref(msg);
+
+ return EOK;
+}
+
+static int nss_dp_get_reply(DBusPendingCall *pending,
+ dbus_uint16_t *err_maj,
+ dbus_uint32_t *err_min,
+ const char **err_msg)
+{
+ DBusMessage *reply;
+ DBusError dbus_error;
+ dbus_bool_t ret;
+ int type;
+ int err = EOK;
+
+ dbus_error_init(&dbus_error);
+
+ reply = dbus_pending_call_steal_reply(pending);
+ if (!reply) {
+ /* reply should never be null. This function shouldn't be called
+ * until reply is valid or timeout has occurred. If reply is NULL
+ * here, something is seriously wrong and we should bail out.
+ */
+ DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n"));
+
+ /* FIXME: Destroy this connection ? */
+ err = EIO;
+ goto done;
+ }
+
+ type = dbus_message_get_type(reply);
+ switch (type) {
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ ret = dbus_message_get_args(reply, &dbus_error,
+ DBUS_TYPE_UINT16, err_maj,
+ DBUS_TYPE_UINT32, err_min,
+ DBUS_TYPE_STRING, err_msg,
+ DBUS_TYPE_INVALID);
+ if (!ret) {
+ DEBUG(1,("Filed to parse message\n"));
+ /* FIXME: Destroy this connection ? */
+ if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
+ err = EIO;
+ goto done;
+ }
+
+ DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n",
+ (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg));
+
+ break;
+
+ case DBUS_MESSAGE_TYPE_ERROR:
+ DEBUG(0,("The Data Provider returned an error [%s]\n",
+ dbus_message_get_error_name(reply)));
+ /* Falling through to default intentionally*/
+ default:
+ /*
+ * Timeout or other error occurred or something
+ * unexpected happened.
+ * It doesn't matter which, because either way we
+ * know that this connection isn't trustworthy.
+ * We'll destroy it now.
+ */
+
+ /* FIXME: Destroy this connection ? */
+ err = EIO;
+ }
+
+done:
+ dbus_pending_call_unref(pending);
+ dbus_message_unref(reply);
+
+ return err;
+}
+
diff --git a/server/responder/nss/nsssrv.h b/server/responder/nss/nsssrv.h
index 37d3735d..cbbb9cc6 100644
--- a/server/responder/nss/nsssrv.h
+++ b/server/responder/nss/nsssrv.h
@@ -68,24 +68,7 @@ struct nss_packet;
int nss_cmd_execute(struct cli_ctx *cctx);
/* from nsssrv_dp.c */
-#define NSS_DP_USER 1
-#define NSS_DP_GROUP 2
-#define NSS_DP_INITGROUPS 3
-
-typedef void (*nss_dp_callback_t)(uint16_t err_maj, uint32_t err_min,
- const char *err_msg, void *ptr);
-
-int nss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *memctx,
- nss_dp_callback_t callback, void *callback_ctx,
- int timeout, const char *domain, int type,
- const char *opt_name, uint32_t opt_id);
-
struct sbus_method *get_nss_dp_methods(void);
struct sss_cmd_table *get_nss_cmds(void);
-int nss_parse_name(TALLOC_CTX *memctx,
- struct nss_ctx *nctx,
- const char *origname,
- const char **domain, const char **name);
-
#endif /* __NSSSRV_H__ */
diff --git a/server/responder/nss/nsssrv_dp.c b/server/responder/nss/nsssrv_dp.c
index b2e15df3..943a872c 100644
--- a/server/responder/nss/nsssrv_dp.c
+++ b/server/responder/nss/nsssrv_dp.c
@@ -28,267 +28,6 @@
#include "sbus/sbus_client.h"
#include "providers/dp_sbus.h"
-struct nss_dp_req {
- nss_dp_callback_t callback;
- void *callback_ctx;
- struct tevent_timer *te;
- DBusPendingCall *pending_reply;
-};
-
-static int nss_dp_req_destructor(void *ptr)
-{
- struct nss_dp_req *req = talloc_get_type(ptr, struct nss_dp_req);
-
- if (req->pending_reply) {
- dbus_pending_call_cancel(req->pending_reply);
- }
-
- return 0;
-}
-
-static void nss_dp_send_acct_timeout(struct tevent_context *ev,
- struct tevent_timer *te,
- struct timeval t, void *data)
-{
- struct nss_dp_req *ndp_req;
- dbus_uint16_t err_maj = DP_ERR_TIMEOUT;
- dbus_uint32_t err_min = EIO;
- const char *err_msg = "Request timed out";
-
- ndp_req = talloc_get_type(data, struct nss_dp_req);
-
- ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx);
-
- talloc_free(ndp_req);
-}
-
-static int nss_dp_get_reply(DBusPendingCall *pending,
- dbus_uint16_t *err_maj,
- dbus_uint32_t *err_min,
- const char **err_msg);
-
-static void nss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr)
-{
- struct nss_dp_req *ndp_req;
- dbus_uint16_t err_maj;
- dbus_uint32_t err_min;
- const char *err_msg;
- int ret;
-
- ndp_req = talloc_get_type(ptr, struct nss_dp_req);
-
- /* free timeout event and remove request destructor */
- talloc_free(ndp_req->te);
- talloc_set_destructor(ndp_req, NULL);
-
- ret = nss_dp_get_reply(pending, &err_maj, &err_min, &err_msg);
- if (ret != EOK) {
- err_maj = DP_ERR_FATAL;
- err_min = ret;
- err_msg = "Failed to get reply from Data Provider";
- }
-
- ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx);
-
- talloc_free(ndp_req);
-}
-
-int nss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *memctx,
- nss_dp_callback_t callback, void *callback_ctx,
- int timeout, const char *domain, int type,
- const char *opt_name, uint32_t opt_id)
-{
- struct nss_dp_req *ndp_req;
- DBusMessage *msg;
- DBusPendingCall *pending_reply;
- DBusConnection *conn;
- dbus_bool_t ret;
- uint32_t be_type;
- const char *attrs = "core";
- char *filter;
- struct timeval tv;
-
- /* either, or, not both */
- if (opt_name && opt_id) {
- return EINVAL;
- }
-
- if (!domain) {
- return EINVAL;
- }
-
- switch (type) {
- case NSS_DP_USER:
- be_type = BE_REQ_USER;
- break;
- case NSS_DP_GROUP:
- be_type = BE_REQ_GROUP;
- break;
- case NSS_DP_INITGROUPS:
- be_type = BE_REQ_INITGROUPS;
- break;
- default:
- return EINVAL;
- }
-
- if (opt_name) {
- filter = talloc_asprintf(memctx, "name=%s", opt_name);
- } else if (opt_id) {
- filter = talloc_asprintf(memctx, "idnumber=%u", opt_id);
- } else {
- filter = talloc_strdup(memctx, "name=*");
- }
- if (!filter) {
- return ENOMEM;
- }
-
- /* double check dp_ctx has actually been initialized.
- * in some pathological cases it may happen that nss starts up before
- * dp connection code is actually able to establish a connection.
- */
- if (!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(rctx->dp_ctx->scon_ctx);
-
- /* create the message */
- msg = dbus_message_new_method_call(NULL,
- DP_CLI_PATH,
- DP_CLI_INTERFACE,
- DP_SRV_METHOD_GETACCTINFO);
- if (msg == NULL) {
- DEBUG(0,("Out of memory?!\n"));
- return ENOMEM;
- }
-
- DEBUG(4, ("Sending request for [%s][%u][%s][%s]\n",
- domain, be_type, attrs, filter));
-
- ret = dbus_message_append_args(msg,
- DBUS_TYPE_STRING, &domain,
- DBUS_TYPE_UINT32, &be_type,
- DBUS_TYPE_STRING, &attrs,
- DBUS_TYPE_STRING, &filter,
- DBUS_TYPE_INVALID);
- if (!ret) {
- DEBUG(1,("Failed to build message\n"));
- return EIO;
- }
-
- ret = dbus_connection_send_with_reply(conn, msg, &pending_reply,
- 600000 /* TODO: set timeout */);
- if (!ret) {
- /*
- * Critical Failure
- * We can't communicate on this connection
- * We'll drop it using the default destructor.
- */
- DEBUG(0, ("D-BUS send failed.\n"));
- dbus_message_unref(msg);
- return EIO;
- }
-
- ndp_req = talloc_zero(memctx, struct nss_dp_req);
- if (!ndp_req) {
- dbus_message_unref(msg);
- return ENOMEM;
- }
- ndp_req->callback = callback;
- ndp_req->callback_ctx = callback_ctx;
-
- /* set up destructor */
- ndp_req->pending_reply = pending_reply;
- talloc_set_destructor((TALLOC_CTX *)ndp_req, nss_dp_req_destructor);
-
- /* setup the timeout handler */
- gettimeofday(&tv, NULL);
- tv.tv_sec += timeout/1000;
- tv.tv_usec += (timeout%1000) * 1000;
- ndp_req->te = tevent_add_timer(rctx->ev, memctx, tv,
- nss_dp_send_acct_timeout, ndp_req);
-
- /* Set up the reply handler */
- dbus_pending_call_set_notify(pending_reply,
- nss_dp_send_acct_callback,
- ndp_req, NULL);
- dbus_message_unref(msg);
-
- return EOK;
-}
-
-static int nss_dp_get_reply(DBusPendingCall *pending,
- dbus_uint16_t *err_maj,
- dbus_uint32_t *err_min,
- const char **err_msg)
-{
- DBusMessage *reply;
- DBusError dbus_error;
- dbus_bool_t ret;
- int type;
- int err = EOK;
-
- dbus_error_init(&dbus_error);
-
- reply = dbus_pending_call_steal_reply(pending);
- if (!reply) {
- /* reply should never be null. This function shouldn't be called
- * until reply is valid or timeout has occurred. If reply is NULL
- * here, something is seriously wrong and we should bail out.
- */
- DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n"));
-
- /* FIXME: Destroy this connection ? */
- err = EIO;
- goto done;
- }
-
- type = dbus_message_get_type(reply);
- switch (type) {
- case DBUS_MESSAGE_TYPE_METHOD_RETURN:
- ret = dbus_message_get_args(reply, &dbus_error,
- DBUS_TYPE_UINT16, err_maj,
- DBUS_TYPE_UINT32, err_min,
- DBUS_TYPE_STRING, err_msg,
- DBUS_TYPE_INVALID);
- if (!ret) {
- DEBUG(1,("Filed to parse message\n"));
- /* FIXME: Destroy this connection ? */
- if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
- err = EIO;
- goto done;
- }
-
- DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n",
- (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg));
-
- break;
-
- case DBUS_MESSAGE_TYPE_ERROR:
- DEBUG(0,("The Data Provider returned an error [%s]\n",
- dbus_message_get_error_name(reply)));
- /* Falling through to default intentionally*/
- default:
- /*
- * Timeout or other error occurred or something
- * unexpected happened.
- * It doesn't matter which, because either way we
- * know that this connection isn't trustworthy.
- * We'll destroy it now.
- */
-
- /* FIXME: Destroy this connection ? */
- err = EIO;
- }
-
-done:
- dbus_pending_call_unref(pending);
- dbus_message_unref(reply);
-
- return err;
-}
-
static int nss_dp_identity(DBusMessage *message, struct sbus_conn_ctx *sconn)
{
dbus_uint16_t version = DATA_PROVIDER_VERSION;
diff --git a/server/responder/pam/pamsrv.h b/server/responder/pam/pamsrv.h
index c751ceed..f33c5dc7 100644
--- a/server/responder/pam/pamsrv.h
+++ b/server/responder/pam/pamsrv.h
@@ -19,6 +19,9 @@ struct pam_auth_req {
struct pam_data *pd;
pam_dp_callback_t *callback;
+
+ bool check_provider;
+ void *data;
};
struct sbus_method *register_pam_dp_methods(void);
diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c
index 2190b566..e229862c 100644
--- a/server/responder/pam/pamsrv_cmd.c
+++ b/server/responder/pam/pamsrv_cmd.c
@@ -20,14 +20,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <errno.h>
-#include <talloc.h>
-
+#include <time.h>
#include "util/util.h"
#include "confdb/confdb.h"
#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
#include "providers/data_provider.h"
#include "responder/pam/pamsrv.h"
+#include "db/sysdb.h"
static int pam_parse_in_data(struct sss_names_ctx *snctx,
struct pam_data *pd,
@@ -252,14 +252,25 @@ done:
sss_cmd_done(cctx, preq);
}
+static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
+ const char *err_msg, void *ptr);
+static void pam_check_user_callback(void *ptr, int status,
+ struct ldb_result *res);
+static void pam_dom_forwarder(struct pam_auth_req *preq);
+
+/* TODO: we should probably return some sort of cookie that is set in the
+ * PAM_ENVIRONMENT, so that we can save performing some calls and cache
+ * data. */
+
static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
{
struct sss_domain_info *dom;
+ struct pam_auth_req *preq;
+ struct pam_data *pd;
uint8_t *body;
size_t blen;
+ int timeout;
int ret;
- struct pam_auth_req *preq;
- struct pam_data *pd;
preq = talloc_zero(cctx, struct pam_auth_req);
if (!preq) {
@@ -279,42 +290,328 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
((uint32_t *)(&body[blen - sizeof(uint32_t)]))[0] != END_OF_PAM_REQUEST) {
DEBUG(1, ("Received data not terminated.\n"));
talloc_free(preq);
- return EINVAL;
+ ret = EINVAL;
+ goto done;
}
pd->cmd = pam_cmd;
ret = pam_parse_in_data(cctx->rctx->names, pd, body, blen);
- if (ret != 0) {
+ if (ret != EOK) {
talloc_free(preq);
- return EINVAL;
+ ret = EINVAL;
+ goto done;
}
+ /* now check user is valid */
if (pd->domain) {
for (dom = cctx->rctx->domains; dom; dom = dom->next) {
if (strcasecmp(dom->name, pd->domain) == 0) break;
}
if (!dom) {
talloc_free(preq);
- return EINVAL;
+ ret = ENOENT;
+ goto done;
}
preq->domain = dom;
}
else {
- DEBUG(4, ("Domain not provided, using default.\n"));
- preq->domain = cctx->rctx->domains;
- pd->domain = preq->domain->name;
+ for (dom = preq->cctx->rctx->domains; dom; dom = dom->next) {
+ if (dom->fqnames) continue;
+
+/* FIXME: need to support negative cache */
+#if HAVE_NEG_CACHE
+ ncret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
+ dom->name, cmdctx->name);
+ if (ncret == ENOENT) break;
+#endif
+ break;
+ }
+ if (!dom) {
+ ret = ENOENT;
+ goto done;
+ }
+ preq->domain = dom;
+ }
+
+ /* When auth is requested always search the provider first,
+ * do not rely on cached data unless the provider is completely
+ * offline */
+ if (preq->domain->provider &&
+ (pam_cmd == SSS_PAM_AUTHENTICATE || pam_cmd == SSS_PAM_SETCRED)) {
+
+ /* no need to re-check later on */
+ preq->check_provider = false;
+ timeout = SSS_CLI_SOCKET_TIMEOUT/2;
+
+ ret = nss_dp_send_acct_req(preq->cctx->rctx, preq,
+ pam_check_user_dp_callback, preq,
+ timeout, preq->domain->name, NSS_DP_USER,
+ preq->pd->user, 0);
+ }
+ else {
+ preq->check_provider = (preq->domain->provider != NULL);
+
+ ret = sysdb_getpwnam(preq, cctx->rctx->sysdb,
+ preq->domain, preq->pd->user,
+ pam_check_user_callback, preq);
+ }
+
+done:
+ if (ret != EOK) {
+ switch (ret) {
+ case ENOENT:
+ pd->pam_status = PAM_USER_UNKNOWN;
+ default:
+ pd->pam_status = PAM_SYSTEM_ERR;
+ }
+ pam_reply(preq);
+ }
+ return EOK;
+}
+
+static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
+ const char *err_msg, void *ptr)
+{
+ struct pam_auth_req *preq = talloc_get_type(ptr, struct pam_auth_req);
+ struct ldb_result *res = NULL;
+ int ret;
+
+ if ((err_maj != DP_ERR_OK) && (err_maj != DP_ERR_OFFLINE)) {
+ DEBUG(2, ("Unable to get information from Data Provider\n"
+ "Error: %u, %u, %s\n",
+ (unsigned int)err_maj, (unsigned int)err_min, err_msg));
+ ret = EFAULT;
+ goto done;
+ }
+
+ if (err_maj == DP_ERR_OFFLINE) {
+ if (preq->data) res = talloc_get_type(preq->data, struct ldb_result);
+ if (!res) res = talloc_zero(preq, struct ldb_result);
+ if (!res) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ pam_check_user_callback(preq, LDB_SUCCESS, res);
+ return;
+ }
+
+ ret = sysdb_getpwnam(preq, preq->cctx->rctx->sysdb,
+ preq->domain, preq->pd->user,
+ pam_check_user_callback, preq);
+
+done:
+ if (ret != EOK) {
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ }
+}
+
+static void pam_check_user_callback(void *ptr, int status,
+ struct ldb_result *res)
+{
+ struct pam_auth_req *preq = talloc_get_type(ptr, struct pam_auth_req);
+ struct sss_domain_info *dom;
+ uint64_t lastUpdate;
+ bool call_provider = false;
+ int timeout;
+ int ret;
+
+ if (status != LDB_SUCCESS) {
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ return;
+ }
+
+ timeout = SSS_CLI_SOCKET_TIMEOUT/2;
+
+ if (preq->check_provider) {
+ switch (res->count) {
+ case 0:
+ call_provider = true;
+ break;
+
+ case 1:
+ timeout = 30; /* FIXME: read from conf */
+
+ lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0],
+ SYSDB_LAST_UPDATE, 0);
+ if (lastUpdate + timeout < time(NULL)) {
+ call_provider = true;
+ }
+ break;
+
+ default:
+ DEBUG(1, ("check user call returned more than one result !?!\n"));
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ return;
+ }
+ }
+
+ if (call_provider) {
+
+ /* dont loop forever :-) */
+ preq->check_provider = false;
+
+ /* keep around current data in case backend is offline */
+ if (res->count) {
+ preq->data = talloc_steal(preq, res);
+ }
+
+ ret = nss_dp_send_acct_req(preq->cctx->rctx, preq,
+ pam_check_user_dp_callback, preq,
+ timeout, preq->domain->name, NSS_DP_USER,
+ preq->pd->user, 0);
+ if (ret != EOK) {
+ DEBUG(3, ("Failed to dispatch request: %d(%s)\n",
+ ret, strerror(ret)));
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ }
+ return;
+ }
+
+ switch (res->count) {
+ case 0:
+ if (!preq->pd->domain) {
+ /* search next as the domain was unknown */
+
+ ret = EOK;
+
+ /* skip domains that require FQnames or have negative caches */
+ for (dom = preq->domain->next; dom; dom = dom->next) {
+
+ if (dom->fqnames) continue;
+
+#if HAVE_NEG_CACHE
+ ncret = nss_ncache_check_user(nctx->ncache,
+ nctx->neg_timeout,
+ dom->name, cmdctx->name);
+ if (ncret == ENOENT) break;
+
+ neghit = true;
+#endif
+ break;
+ }
+#if HAVE_NEG_CACHE
+ /* reset neghit if we still have a domain to check */
+ if (dom) neghit = false;
+
+ if (neghit) {
+ DEBUG(2, ("User [%s] does not exist! (negative cache)\n",
+ cmdctx->name));
+ ret = ENOENT;
+ }
+#endif
+ if (dom == NULL) {
+ DEBUG(2, ("No matching domain found for [%s], fail!\n",
+ preq->pd->user));
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ preq->domain = dom;
+ preq->data = NULL;
+
+ DEBUG(4, ("Requesting info for [%s@%s]\n",
+ preq->pd->user, preq->domain->name));
+
+ /* When auth is requested always search the provider first,
+ * do not rely on cached data unless the provider is
+ * completely offline */
+ if (preq->domain->provider &&
+ (preq->pd->cmd == SSS_PAM_AUTHENTICATE ||
+ preq->pd->cmd == SSS_PAM_SETCRED)) {
+
+ /* no need to re-check later on */
+ preq->check_provider = false;
+
+ ret = nss_dp_send_acct_req(preq->cctx->rctx, preq,
+ pam_check_user_dp_callback,
+ preq, timeout,
+ preq->domain->name,
+ NSS_DP_USER,
+ preq->pd->user, 0);
+ }
+ else {
+ preq->check_provider = (preq->domain->provider != NULL);
+
+ ret = sysdb_getpwnam(preq, preq->cctx->rctx->sysdb,
+ preq->domain, preq->pd->user,
+ pam_check_user_callback, preq);
+ }
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to make request to our cache!\n"));
+ }
+ }
+
+ /* we made another call, end here */
+ if (ret == EOK) return;
+ }
+ else {
+ ret = ENOENT;
+ }
+
+ DEBUG(2, ("No results for check user call\n"));
+
+#if HAVE_NEG_CACHE
+ /* set negative cache only if not result of cache check */
+ if (!neghit) {
+ ret = nss_ncache_set_user(nctx->ncache, false,
+ dctx->domain->name, cmdctx->name);
+ if (ret != EOK) {
+ NSS_CMD_FATAL_ERROR(cctx);
+ }
+ }
+#endif
+
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ preq->pd->pam_status = PAM_USER_UNKNOWN;
+ } else {
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ }
+ pam_reply(preq);
+ return;
+ }
+ break;
+
+ case 1:
+
+ /* BINGO */
+ pam_dom_forwarder(preq);
+ return;
+
+ default:
+ DEBUG(1, ("check user call returned more than one result !?!\n"));
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ }
+}
+
+static void pam_dom_forwarder(struct pam_auth_req *preq)
+{
+ int ret;
+
+ if (!preq->pd->domain) {
+ preq->pd->domain = preq->domain->name;
}
if (!preq->domain->provider) {
preq->callback = pam_reply;
- return LOCAL_pam_handler(preq);
- };
-
- preq->callback = pam_reply;
- ret = pam_dp_send_req(preq, PAM_DP_TIMEOUT);
- DEBUG(4, ("pam_dp_send_req returned %d\n", ret));
+ ret = LOCAL_pam_handler(preq);
+ }
+ else {
+ 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;
+ if (ret != EOK) {
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ }
}
static int pam_cmd_authenticate(struct cli_ctx *cctx) {