diff options
-rw-r--r-- | server/responder/common/responder.h | 12 | ||||
-rw-r--r-- | server/responder/common/responder_dp.c | 262 | ||||
-rw-r--r-- | server/responder/nss/nsssrv.h | 17 | ||||
-rw-r--r-- | server/responder/nss/nsssrv_dp.c | 261 | ||||
-rw-r--r-- | server/responder/pam/pamsrv.h | 3 | ||||
-rw-r--r-- | server/responder/pam/pamsrv_cmd.c | 335 |
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) { |