summaryrefslogtreecommitdiffstats
path: root/server/responder/pam
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2009-04-16 18:35:06 -0400
committerSimo Sorce <ssorce@redhat.com>2009-04-17 14:58:46 -0400
commit65951186e13fea410d087c84f7332b04d116f8fc (patch)
treeafc9a8c181d068d518e9e5f75fb7c30ff6db2f53 /server/responder/pam
parent8f209f1cee137e410386b68f82a31c9ba862fe19 (diff)
downloadsssd-65951186e13fea410d087c84f7332b04d116f8fc.tar.gz
sssd-65951186e13fea410d087c84f7332b04d116f8fc.tar.xz
sssd-65951186e13fea410d087c84f7332b04d116f8fc.zip
Force user check and discover user's domain
Force a user lookup against the users domain provider. If a user domain is not specified search though all non fully qualifying domains. Perform authentication against the corrent domain auth backend, based on the user's domain found in the lookup if one was not specified. Also move the NSS-DP functions in COMMON-DP as they are reused by the PAM responder too now.
Diffstat (limited to 'server/responder/pam')
-rw-r--r--server/responder/pam/pamsrv.h3
-rw-r--r--server/responder/pam/pamsrv_cmd.c335
2 files changed, 319 insertions, 19 deletions
diff --git a/server/responder/pam/pamsrv.h b/server/responder/pam/pamsrv.h
index c751ceed5..f33c5dc74 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 2190b5668..e229862c0 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) {