summaryrefslogtreecommitdiffstats
path: root/src/providers/ldap
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2010-02-18 07:49:04 -0500
committerStephen Gallagher <sgallagh@redhat.com>2010-02-18 13:48:45 -0500
commit1c48b5a62f73234ed26bb20f0ab345ab61cda0ab (patch)
tree0b6cddd567a862e1a7b5df23764869782a62ca78 /src/providers/ldap
parent8c56df3176f528fe0260974b3bf934173c4651ea (diff)
downloadsssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.gz
sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.xz
sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.zip
Rename server/ directory to src/
Also update BUILD.txt
Diffstat (limited to 'src/providers/ldap')
-rw-r--r--src/providers/ldap/ldap_auth.c1055
-rw-r--r--src/providers/ldap/ldap_child.c429
-rw-r--r--src/providers/ldap/ldap_common.c589
-rw-r--r--src/providers/ldap/ldap_common.h115
-rw-r--r--src/providers/ldap/ldap_id.c795
-rw-r--r--src/providers/ldap/ldap_id_cleanup.c555
-rw-r--r--src/providers/ldap/ldap_id_enum.c608
-rw-r--r--src/providers/ldap/ldap_init.c179
-rw-r--r--src/providers/ldap/sdap.c388
-rw-r--r--src/providers/ldap/sdap.h258
-rw-r--r--src/providers/ldap/sdap_async.c1018
-rw-r--r--src/providers/ldap/sdap_async.h126
-rw-r--r--src/providers/ldap/sdap_async_accounts.c2065
-rw-r--r--src/providers/ldap/sdap_async_connection.c1141
-rw-r--r--src/providers/ldap/sdap_async_private.h68
-rw-r--r--src/providers/ldap/sdap_child_helpers.c462
16 files changed, 9851 insertions, 0 deletions
diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
new file mode 100644
index 000000000..cfe8adb97
--- /dev/null
+++ b/src/providers/ldap/ldap_auth.c
@@ -0,0 +1,1055 @@
+/*
+ SSSD
+
+ LDAP Backend Module
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2008 Red Hat
+
+ 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/>.
+*/
+
+#ifdef WITH_MOZLDAP
+#define LDAP_OPT_SUCCESS LDAP_SUCCESS
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID ((ber_tag_t) 0x80U)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ((ber_tag_t) 0x81U)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ((ber_tag_t) 0x82U)
+#endif
+
+#define _XOPEN_SOURCE 500 /* for strptime() */
+#include <time.h>
+#undef _XOPEN_SOURCE
+#include <errno.h>
+#include <sys/time.h>
+#include <strings.h>
+
+#include <shadow.h>
+#include <security/pam_modules.h>
+
+#include "util/util.h"
+#include "util/user_info_msg.h"
+#include "db/sysdb.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async.h"
+
+enum pwexpire {
+ PWEXPIRE_NONE = 0,
+ PWEXPIRE_LDAP_PASSWORD_POLICY,
+ PWEXPIRE_KERBEROS,
+ PWEXPIRE_SHADOW
+};
+
+static errno_t check_pwexpire_kerberos(const char *expire_date, time_t now,
+ enum sdap_result *result)
+{
+ char *end;
+ struct tm tm = {0, 0, 0, 0, 0, 0, 0, 0, 0};
+ time_t expire_time;
+
+ *result = SDAP_AUTH_FAILED;
+
+ end = strptime(expire_date, "%Y%m%d%H%M%SZ", &tm);
+ if (end == NULL) {
+ DEBUG(1, ("Kerberos expire date [%s] invalid.\n", expire_date));
+ return EINVAL;
+ }
+ if (*end != '\0') {
+ DEBUG(1, ("Kerberos expire date [%s] contains extra characters.\n",
+ expire_date));
+ return EINVAL;
+ }
+
+ expire_time = mktime(&tm);
+ if (expire_time == -1) {
+ DEBUG(1, ("mktime failed to convert [%s].\n", expire_date));
+ return EINVAL;
+ }
+
+ tzset();
+ expire_time -= timezone;
+ DEBUG(9, ("Time info: tzname[0] [%s] tzname[1] [%s] timezone [%d] "
+ "daylight [%d] now [%d] expire_time [%d].\n", tzname[0],
+ tzname[1], timezone, daylight, now, expire_time));
+
+ if (difftime(now, expire_time) > 0.0) {
+ DEBUG(4, ("Kerberos password expired.\n"));
+ *result = SDAP_AUTH_PW_EXPIRED;
+ } else {
+ *result = SDAP_AUTH_SUCCESS;
+ }
+
+ return EOK;
+}
+
+static errno_t check_pwexpire_shadow(struct spwd *spwd, time_t now,
+ enum sdap_result *result)
+{
+ long today;
+ long password_age;
+
+ if (spwd->sp_lstchg <= 0) {
+ DEBUG(4, ("Last change day is not set, new password needed.\n"));
+ *result = SDAP_AUTH_PW_EXPIRED;
+ return EOK;
+ }
+
+ today = (long) (now / (60 * 60 *24));
+ password_age = today - spwd->sp_lstchg;
+ if (password_age < 0) {
+ DEBUG(2, ("The last password change time is in the future!.\n"));
+ *result = SDAP_AUTH_SUCCESS;
+ return EOK;
+ }
+
+ if ((spwd->sp_expire != -1 && today > spwd->sp_expire) ||
+ (spwd->sp_max != -1 && spwd->sp_inact != -1 &&
+ password_age > spwd->sp_max + spwd->sp_inact))
+ {
+ DEBUG(4, ("Account expired.\n"));
+ *result = SDAP_ACCT_EXPIRED;
+ return EOK;
+ }
+
+ if (spwd->sp_max != -1 && password_age > spwd->sp_max) {
+ DEBUG(4, ("Password expired.\n"));
+ *result = SDAP_AUTH_PW_EXPIRED;
+ return EOK;
+ }
+
+/* TODO: evaluate spwd->min and spwd->warn */
+
+ *result = SDAP_AUTH_SUCCESS;
+ return EOK;
+}
+
+static errno_t string_to_shadowpw_days(const char *s, long *d)
+{
+ long l;
+ char *endptr;
+
+ if (s == NULL || *s == '\0') {
+ *d = -1;
+ return EOK;
+ }
+
+ errno = 0;
+ l = strtol(s, &endptr, 10);
+ if (errno != 0) {
+ DEBUG(1, ("strtol failed [%d][%s].\n", errno, strerror(errno)));
+ return errno;
+ }
+
+ if (*endptr != '\0') {
+ DEBUG(1, ("Input string [%s] is invalid.\n", s));
+ return EINVAL;
+ }
+
+ if (*d < -1) {
+ DEBUG(1, ("Input string contains not allowed negative value [%d].\n",
+ *d));
+ return EINVAL;
+ }
+
+ *d = l;
+
+ return EOK;
+}
+
+static errno_t find_password_expiration_attributes(TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg,
+ struct dp_option *opts,
+ enum pwexpire *type, void **data)
+{
+ const char *mark;
+ const char *val;
+ struct spwd *spwd;
+ const char *pwd_policy;
+ int ret;
+
+ *type = PWEXPIRE_NONE;
+ *data = NULL;
+
+ pwd_policy = dp_opt_get_string(opts, SDAP_PWD_POLICY);
+ if (pwd_policy == NULL) {
+ DEBUG(1, ("Missing password policy.\n"));
+ return EINVAL;
+ }
+
+ mark = ldb_msg_find_attr_as_string(msg, SYSDB_PWD_ATTRIBUTE, NULL);
+ if (mark != NULL) {
+ DEBUG(9, ("Found pwdAttribute, "
+ "assuming LDAP password policies are active.\n"));
+
+ *type = PWEXPIRE_LDAP_PASSWORD_POLICY;
+ return EOK;
+ }
+
+ if (strcasecmp(pwd_policy, PWD_POL_OPT_NONE) == 0) {
+ DEBUG(9, ("No password policy requested.\n"));
+ return EOK;
+ } else if (strcasecmp(pwd_policy, PWD_POL_OPT_MIT) == 0) {
+ mark = ldb_msg_find_attr_as_string(msg, SYSDB_KRBPW_LASTCHANGE, NULL);
+ if (mark != NULL) {
+ DEBUG(9, ("Found Kerberos password expiration attributes.\n"))
+ val = ldb_msg_find_attr_as_string(msg, SYSDB_KRBPW_EXPIRATION,
+ NULL);
+ if (val != NULL) {
+ *data = talloc_strdup(mem_ctx, val);
+ if (*data == NULL) {
+ DEBUG(1, ("talloc_strdup failed.\n"));
+ return ENOMEM;
+ }
+ *type = PWEXPIRE_KERBEROS;
+
+ return EOK;
+ }
+ } else {
+ DEBUG(1, ("No Kerberos password expiration attributes found, "
+ "but MIT Kerberos password policy was requested.\n"));
+ return EINVAL;
+ }
+ } else if (strcasecmp(pwd_policy, PWD_POL_OPT_SHADOW) == 0) {
+ mark = ldb_msg_find_attr_as_string(msg, SYSDB_SHADOWPW_LASTCHANGE, NULL);
+ if (mark != NULL) {
+ DEBUG(9, ("Found shadow password expiration attributes.\n"))
+ spwd = talloc_zero(mem_ctx, struct spwd);
+ if (spwd == NULL) {
+ DEBUG(1, ("talloc failed.\n"));
+ return ENOMEM;
+ }
+
+ val = ldb_msg_find_attr_as_string(msg, SYSDB_SHADOWPW_LASTCHANGE, NULL);
+ ret = string_to_shadowpw_days(val, &spwd->sp_lstchg);
+ if (ret != EOK) goto shadow_fail;
+
+ val = ldb_msg_find_attr_as_string(msg, SYSDB_SHADOWPW_MIN, NULL);
+ ret = string_to_shadowpw_days(val, &spwd->sp_min);
+ if (ret != EOK) goto shadow_fail;
+
+ val = ldb_msg_find_attr_as_string(msg, SYSDB_SHADOWPW_MAX, NULL);
+ ret = string_to_shadowpw_days(val, &spwd->sp_max);
+ if (ret != EOK) goto shadow_fail;
+
+ val = ldb_msg_find_attr_as_string(msg, SYSDB_SHADOWPW_WARNING, NULL);
+ ret = string_to_shadowpw_days(val, &spwd->sp_warn);
+ if (ret != EOK) goto shadow_fail;
+
+ val = ldb_msg_find_attr_as_string(msg, SYSDB_SHADOWPW_INACTIVE, NULL);
+ ret = string_to_shadowpw_days(val, &spwd->sp_inact);
+ if (ret != EOK) goto shadow_fail;
+
+ val = ldb_msg_find_attr_as_string(msg, SYSDB_SHADOWPW_EXPIRE, NULL);
+ ret = string_to_shadowpw_days(val, &spwd->sp_expire);
+ if (ret != EOK) goto shadow_fail;
+
+ *data = spwd;
+ *type = PWEXPIRE_SHADOW;
+
+ return EOK;
+ } else {
+ DEBUG(1, ("No shadow password attributes found, "
+ "but shadow password policy was requested.\n"));
+ return EINVAL;
+ }
+ }
+
+ DEBUG(9, ("No password expiration attributes found.\n"));
+ return EOK;
+
+shadow_fail:
+ talloc_free(spwd);
+ return ret;
+}
+
+/* ==Get-User-DN========================================================== */
+
+struct get_user_dn_state {
+ struct tevent_context *ev;
+ struct sdap_auth_ctx *ctx;
+ struct sdap_handle *sh;
+
+ const char **attrs;
+ const char *name;
+
+ char *dn;
+ enum pwexpire pw_expire_type;
+ void *pw_expire_data;
+};
+
+static void get_user_dn_done(void *pvt, int err, struct ldb_result *res);
+
+struct tevent_req *get_user_dn_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_auth_ctx *ctx,
+ struct sdap_handle *sh,
+ const char *username)
+{
+ struct tevent_req *req;
+ struct get_user_dn_state *state;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct get_user_dn_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sh = sh;
+ state->name = username;
+
+ state->attrs = talloc_array(state, const char *, 11);
+ if (!state->attrs) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ state->attrs[0] = SYSDB_ORIG_DN;
+ state->attrs[1] = SYSDB_SHADOWPW_LASTCHANGE;
+ state->attrs[2] = SYSDB_SHADOWPW_MIN;
+ state->attrs[3] = SYSDB_SHADOWPW_MAX;
+ state->attrs[4] = SYSDB_SHADOWPW_WARNING;
+ state->attrs[5] = SYSDB_SHADOWPW_INACTIVE;
+ state->attrs[6] = SYSDB_SHADOWPW_EXPIRE;
+ state->attrs[7] = SYSDB_KRBPW_LASTCHANGE;
+ state->attrs[8] = SYSDB_KRBPW_EXPIRATION;
+ state->attrs[9] = SYSDB_PWD_ATTRIBUTE;
+ state->attrs[10] = NULL;
+
+ /* this sysdb call uses a sysdn operation, which means it will be
+ * schedule only after we return, no timer hack needed */
+ ret = sysdb_get_user_attr(state, state->ctx->be->sysdb,
+ state->ctx->be->domain, state->name,
+ state->attrs, get_user_dn_done, req);
+ if (ret) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void get_user_dn_done(void *pvt, int err, struct ldb_result *res)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct get_user_dn_state *state = tevent_req_data(req,
+ struct get_user_dn_state);
+ const char *dn;
+ int ret;
+
+ if (err != LDB_SUCCESS) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ switch (res->count) {
+ case 0:
+ /* FIXME: not in cache, needs a true search */
+ tevent_req_error(req, ENOENT);
+ break;
+
+ case 1:
+ dn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_ORIG_DN, NULL);
+ if (!dn) {
+ /* TODO: try to search ldap server ? */
+
+ /* FIXME: remove once we store originalDN on every call
+ * NOTE: this is wrong, works only with some DITs */
+ dn = talloc_asprintf(state, "%s=%s,%s",
+ state->ctx->opts->user_map[SDAP_AT_USER_NAME].name,
+ state->name,
+ dp_opt_get_string(state->ctx->opts->basic,
+ SDAP_USER_SEARCH_BASE));
+ if (!dn) {
+ tevent_req_error(req, ENOMEM);
+ break;
+ }
+ }
+
+ state->dn = talloc_strdup(state, dn);
+ if (!state->dn) {
+ tevent_req_error(req, ENOMEM);
+ break;
+ }
+
+ ret = find_password_expiration_attributes(state, res->msgs[0],
+ state->ctx->opts->basic,
+ &state->pw_expire_type,
+ &state->pw_expire_data);
+ if (ret != EOK) {
+ DEBUG(1, ("find_password_expiration_attributes failed.\n"));
+ tevent_req_error(req, ENOMEM);
+ break;
+ }
+
+ tevent_req_done(req);
+ break;
+
+ default:
+ DEBUG(1, ("A user search by name (%s) returned > 1 results!\n",
+ state->name));
+ tevent_req_error(req, EFAULT);
+ break;
+ }
+}
+
+static int get_user_dn_recv(struct tevent_req *req,
+ TALLOC_CTX *memctx, char **dn,
+ enum pwexpire *pw_expire_type,
+ void **pw_expire_data)
+{
+ struct get_user_dn_state *state = tevent_req_data(req,
+ struct get_user_dn_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *dn = talloc_steal(memctx, state->dn);
+ if (!*dn) return ENOMEM;
+
+ /* state->pw_expire_data may be NULL */
+ *pw_expire_data = talloc_steal(memctx, state->pw_expire_data);
+
+ *pw_expire_type = state->pw_expire_type;
+
+ return EOK;
+}
+
+/* ==Authenticate-User==================================================== */
+
+struct auth_state {
+ struct tevent_context *ev;
+ struct sdap_auth_ctx *ctx;
+ const char *username;
+ struct dp_opt_blob password;
+
+ struct sdap_handle *sh;
+
+ enum sdap_result result;
+ char *dn;
+ enum pwexpire pw_expire_type;
+ void *pw_expire_data;
+
+ struct fo_server *srv;
+};
+
+static void auth_resolve_done(struct tevent_req *subreq);
+static void auth_connect_done(struct tevent_req *subreq);
+static void auth_get_user_dn_done(struct tevent_req *subreq);
+static void auth_bind_user_done(struct tevent_req *subreq);
+
+static struct tevent_req *auth_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_auth_ctx *ctx,
+ const char *username,
+ struct dp_opt_blob password)
+{
+ struct tevent_req *req, *subreq;
+ struct auth_state *state;
+
+ req = tevent_req_create(memctx, &state, struct auth_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->username = username;
+ state->password = password;
+ state->srv = NULL;
+
+ subreq = be_resolve_server_send(state, ev, ctx->be, ctx->service->name);
+ if (!subreq) goto fail;
+
+ tevent_req_set_callback(subreq, auth_resolve_done, req);
+
+ return req;
+
+fail:
+ talloc_zfree(req);
+ return NULL;
+}
+
+static void auth_resolve_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct auth_state *state = tevent_req_data(req,
+ struct auth_state);
+ int ret;
+
+ ret = be_resolve_server_recv(subreq, &state->srv);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_connect_send(state, state->ev, state->ctx->opts,
+ state->ctx->service->uri, true);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, auth_connect_done, req);
+}
+
+static void auth_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct auth_state *state = tevent_req_data(req,
+ struct auth_state);
+ int ret;
+
+ ret = sdap_connect_recv(subreq, state, &state->sh);
+ talloc_zfree(subreq);
+ if (ret) {
+ if (state->srv) {
+ /* mark this server as bad if connection failed */
+ fo_set_port_status(state->srv, PORT_NOT_WORKING);
+ }
+
+ tevent_req_error(req, ret);
+ return;
+ } else if (state->srv) {
+ fo_set_port_status(state->srv, PORT_WORKING);
+ }
+
+ subreq = get_user_dn_send(state, state->ev,
+ state->ctx, state->sh,
+ state->username);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, auth_get_user_dn_done, req);
+}
+
+static void auth_get_user_dn_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct auth_state *state = tevent_req_data(req,
+ struct auth_state);
+ int ret;
+
+ ret = get_user_dn_recv(subreq, state, &state->dn, &state->pw_expire_type,
+ &state->pw_expire_data);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_auth_send(state, state->ev, state->sh,
+ NULL, NULL, state->dn,
+ "password", state->password);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, auth_bind_user_done, req);
+}
+
+static void auth_bind_user_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct auth_state *state = tevent_req_data(req,
+ struct auth_state);
+ int ret;
+
+ ret = sdap_auth_recv(subreq, &state->result);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int auth_recv(struct tevent_req *req,
+ TALLOC_CTX *memctx,
+ struct sdap_handle **sh,
+ enum sdap_result *result, char **dn,
+ enum pwexpire *pw_expire_type, void **pw_expire_data)
+{
+ struct auth_state *state = tevent_req_data(req, struct auth_state);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ switch (tstate) {
+ case TEVENT_REQ_USER_ERROR:
+ if (err == ETIMEDOUT) *result = SDAP_UNAVAIL;
+ else *result = SDAP_ERROR;
+ return err;
+ default:
+ *result = SDAP_ERROR;
+ return EIO;
+ }
+ }
+
+ if (sh != NULL) {
+ *sh = talloc_steal(memctx, state->sh);
+ if (*sh == NULL) return ENOMEM;
+ }
+
+ if (dn != NULL) {
+ *dn = talloc_steal(memctx, state->dn);
+ if (*dn == NULL) return ENOMEM;
+ }
+
+ if (pw_expire_data != NULL) {
+ *pw_expire_data = talloc_steal(memctx, state->pw_expire_data);
+ }
+
+ *pw_expire_type = state->pw_expire_type;
+
+ *result = state->result;
+ return EOK;
+}
+
+/* ==Perform-Password-Change===================== */
+
+struct sdap_pam_chpass_state {
+ struct be_req *breq;
+ struct pam_data *pd;
+ const char *username;
+ char *dn;
+ char *password;
+ char *new_password;
+ struct sdap_handle *sh;
+};
+
+static void sdap_auth4chpass_done(struct tevent_req *req);
+static void sdap_pam_chpass_done(struct tevent_req *req);
+static void sdap_pam_auth_reply(struct be_req *breq, int dp_err, int result);
+
+void sdap_pam_chpass_handler(struct be_req *breq)
+{
+ struct sdap_pam_chpass_state *state;
+ struct sdap_auth_ctx *ctx;
+ struct tevent_req *subreq;
+ struct pam_data *pd;
+ struct dp_opt_blob authtok;
+ int dp_err = DP_ERR_FATAL;
+
+ ctx = talloc_get_type(breq->be_ctx->bet_info[BET_CHPASS].pvt_bet_data,
+ struct sdap_auth_ctx);
+ pd = talloc_get_type(breq->req_data, struct pam_data);
+
+ if (be_is_offline(ctx->be)) {
+ DEBUG(4, ("Backend is marked offline, retry later!\n"));
+ pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ dp_err = DP_ERR_OFFLINE;
+ goto done;
+ }
+
+ DEBUG(2, ("starting password change request for user [%s].\n", pd->user));
+
+ pd->pam_status = PAM_SYSTEM_ERR;
+
+ if (pd->cmd != SSS_PAM_CHAUTHTOK && pd->cmd != SSS_PAM_CHAUTHTOK_PRELIM) {
+ DEBUG(2, ("chpass target was called by wrong pam command.\n"));
+ goto done;
+ }
+
+ state = talloc_zero(breq, struct sdap_pam_chpass_state);
+ if (!state) goto done;
+
+ state->breq = breq;
+ state->pd = pd;
+ state->username = pd->user;
+ state->password = talloc_strndup(state,
+ (char *)pd->authtok, pd->authtok_size);
+ if (!state->password) goto done;
+ talloc_set_destructor((TALLOC_CTX *)state->password,
+ password_destructor);
+
+ if (pd->cmd == SSS_PAM_CHAUTHTOK) {
+ state->new_password = talloc_strndup(state,
+ (char *)pd->newauthtok,
+ pd->newauthtok_size);
+ if (!state->new_password) goto done;
+ talloc_set_destructor((TALLOC_CTX *)state->new_password,
+ password_destructor);
+ }
+
+ authtok.data = (uint8_t *)state->password;
+ authtok.length = strlen(state->password);
+ subreq = auth_send(breq, breq->be_ctx->ev,
+ ctx, state->username, authtok);
+ if (!subreq) goto done;
+
+ tevent_req_set_callback(subreq, sdap_auth4chpass_done, state);
+ return;
+
+done:
+ sdap_pam_auth_reply(breq, dp_err, pd->pam_status);
+}
+
+static void sdap_auth4chpass_done(struct tevent_req *req)
+{
+ struct sdap_pam_chpass_state *state =
+ tevent_req_callback_data(req, struct sdap_pam_chpass_state);
+ struct tevent_req *subreq;
+ enum sdap_result result;
+ enum pwexpire pw_expire_type;
+ void *pw_expire_data;
+ int dp_err = DP_ERR_FATAL;
+ int ret;
+
+ ret = auth_recv(req, state, &state->sh,
+ &result, &state->dn,
+ &pw_expire_type, &pw_expire_data);
+ talloc_zfree(req);
+ if (ret) {
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+
+ if (result == SDAP_AUTH_SUCCESS &&
+ state->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
+ DEBUG(9, ("Initial authentication for change password operation "
+ "successful.\n"));
+ state->pd->pam_status = PAM_SUCCESS;
+ goto done;
+ }
+
+ if (result == SDAP_AUTH_SUCCESS) {
+ switch (pw_expire_type) {
+ case PWEXPIRE_SHADOW:
+ ret = check_pwexpire_shadow(pw_expire_data, time(NULL),
+ &result);
+ if (ret != EOK) {
+ DEBUG(1, ("check_pwexpire_shadow failed.\n"));
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+ break;
+ case PWEXPIRE_KERBEROS:
+ ret = check_pwexpire_kerberos(pw_expire_data, time(NULL),
+ &result);
+ if (ret != EOK) {
+ DEBUG(1, ("check_pwexpire_kerberos failed.\n"));
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+
+ if (result == SDAP_AUTH_PW_EXPIRED) {
+ DEBUG(1, ("LDAP provider cannot change kerberos "
+ "passwords.\n"));
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+ break;
+ case PWEXPIRE_LDAP_PASSWORD_POLICY:
+ case PWEXPIRE_NONE:
+ break;
+ default:
+ DEBUG(1, ("Unknow pasword expiration type.\n"));
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+ }
+
+ switch (result) {
+ case SDAP_AUTH_SUCCESS:
+ case SDAP_AUTH_PW_EXPIRED:
+ DEBUG(7, ("user [%s] successfully authenticated.\n", state->dn));
+ if (pw_expire_type == PWEXPIRE_SHADOW) {
+/* TODO: implement async ldap modify request */
+ DEBUG(1, ("Changing shadow password attributes not implemented.\n"));
+ state->pd->pam_status = PAM_MODULE_UNKNOWN;
+ goto done;
+ } else {
+ subreq = sdap_exop_modify_passwd_send(state,
+ state->breq->be_ctx->ev,
+ state->sh,
+ state->dn,
+ state->password,
+ state->new_password);
+
+ if (!subreq) {
+ DEBUG(2, ("Failed to change password for %s\n", state->username));
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sdap_pam_chpass_done, state);
+ return;
+ }
+ break;
+ case SDAP_AUTH_FAILED:
+ state->pd->pam_status = PAM_AUTH_ERR;
+ break;
+ default:
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ }
+
+done:
+ sdap_pam_auth_reply(state->breq, dp_err, state->pd->pam_status);
+}
+
+static void sdap_pam_chpass_done(struct tevent_req *req)
+{
+ struct sdap_pam_chpass_state *state =
+ tevent_req_callback_data(req, struct sdap_pam_chpass_state);
+ enum sdap_result result;
+ int dp_err = DP_ERR_FATAL;
+ int ret;
+ char *user_error_message = NULL;
+ size_t msg_len;
+ uint8_t *msg;
+
+ ret = sdap_exop_modify_passwd_recv(req, state, &result, &user_error_message);
+ talloc_zfree(req);
+ if (ret) {
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+
+ switch (result) {
+ case SDAP_SUCCESS:
+ state->pd->pam_status = PAM_SUCCESS;
+ dp_err = DP_ERR_OK;
+ break;
+ default:
+ state->pd->pam_status = PAM_AUTHTOK_ERR;
+ if (user_error_message != NULL) {
+ ret = pack_user_info_chpass_error(state->pd, user_error_message,
+ &msg_len, &msg);
+ if (ret != EOK) {
+ DEBUG(1, ("pack_user_info_chpass_error failed.\n"));
+ } else {
+ ret = pam_add_response(state->pd, SSS_PAM_USER_INFO, msg_len,
+ msg);
+ if (ret != EOK) {
+ DEBUG(1, ("pam_add_response failed.\n"));
+ }
+ }
+ }
+ }
+
+done:
+ sdap_pam_auth_reply(state->breq, dp_err, state->pd->pam_status);
+}
+/* ==Perform-User-Authentication-and-Password-Caching===================== */
+
+struct sdap_pam_auth_state {
+ struct be_req *breq;
+ struct pam_data *pd;
+ const char *username;
+ struct dp_opt_blob password;
+};
+
+static void sdap_pam_auth_done(struct tevent_req *req);
+static void sdap_password_cache_done(struct tevent_req *req);
+
+void sdap_pam_auth_handler(struct be_req *breq)
+{
+ struct sdap_pam_auth_state *state;
+ struct sdap_auth_ctx *ctx;
+ struct tevent_req *subreq;
+ struct pam_data *pd;
+ int dp_err = DP_ERR_FATAL;
+
+ ctx = talloc_get_type(breq->be_ctx->bet_info[BET_AUTH].pvt_bet_data,
+ struct sdap_auth_ctx);
+ pd = talloc_get_type(breq->req_data, struct pam_data);
+
+ if (be_is_offline(ctx->be)) {
+ DEBUG(4, ("Backend is marked offline, retry later!\n"));
+ pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ dp_err = DP_ERR_OFFLINE;
+ goto done;
+ }
+
+ pd->pam_status = PAM_SYSTEM_ERR;
+
+ switch (pd->cmd) {
+ case SSS_PAM_AUTHENTICATE:
+ case SSS_PAM_CHAUTHTOK_PRELIM:
+
+ state = talloc_zero(breq, struct sdap_pam_auth_state);
+ if (!state) goto done;
+
+ state->breq = breq;
+ state->pd = pd;
+ state->username = pd->user;
+ state->password.data = pd->authtok;
+ state->password.length = pd->authtok_size;
+
+ subreq = auth_send(breq, breq->be_ctx->ev, ctx,
+ state->username, state->password);
+ if (!subreq) goto done;
+
+ tevent_req_set_callback(subreq, sdap_pam_auth_done, state);
+ return;
+
+ case SSS_PAM_CHAUTHTOK:
+ break;
+
+ case SSS_PAM_ACCT_MGMT:
+ case SSS_PAM_SETCRED:
+ case SSS_PAM_OPEN_SESSION:
+ case SSS_PAM_CLOSE_SESSION:
+ pd->pam_status = PAM_SUCCESS;
+ dp_err = DP_ERR_OK;
+ break;
+ default:
+ pd->pam_status = PAM_MODULE_UNKNOWN;
+ dp_err = DP_ERR_OK;
+ }
+
+done:
+ sdap_pam_auth_reply(breq, dp_err, pd->pam_status);
+}
+
+static void sdap_pam_auth_done(struct tevent_req *req)
+{
+ struct sdap_pam_auth_state *state =
+ tevent_req_callback_data(req, struct sdap_pam_auth_state);
+ struct tevent_req *subreq;
+ enum sdap_result result;
+ enum pwexpire pw_expire_type;
+ void *pw_expire_data;
+ int dp_err = DP_ERR_OK;
+ int ret;
+
+ ret = auth_recv(req, state, NULL,
+ &result, NULL,
+ &pw_expire_type, &pw_expire_data);
+ talloc_zfree(req);
+ if (ret) {
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ dp_err = DP_ERR_FATAL;
+ goto done;
+ }
+
+ if (result == SDAP_AUTH_SUCCESS) {
+ switch (pw_expire_type) {
+ case PWEXPIRE_SHADOW:
+ ret = check_pwexpire_shadow(pw_expire_data, time(NULL),
+ &result);
+ if (ret != EOK) {
+ DEBUG(1, ("check_pwexpire_shadow failed.\n"));
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+ break;
+ case PWEXPIRE_KERBEROS:
+ ret = check_pwexpire_kerberos(pw_expire_data, time(NULL),
+ &result);
+ if (ret != EOK) {
+ DEBUG(1, ("check_pwexpire_kerberos failed.\n"));
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+ break;
+ case PWEXPIRE_LDAP_PASSWORD_POLICY:
+ case PWEXPIRE_NONE:
+ break;
+ default:
+ DEBUG(1, ("Unknow pasword expiration type.\n"));
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+ }
+
+ switch (result) {
+ case SDAP_AUTH_SUCCESS:
+ state->pd->pam_status = PAM_SUCCESS;
+ break;
+ case SDAP_AUTH_FAILED:
+ state->pd->pam_status = PAM_PERM_DENIED;
+ break;
+ case SDAP_UNAVAIL:
+ state->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ break;
+ case SDAP_ACCT_EXPIRED:
+ state->pd->pam_status = PAM_ACCT_EXPIRED;
+ break;
+ case SDAP_AUTH_PW_EXPIRED:
+ state->pd->pam_status = PAM_AUTHTOK_EXPIRED;
+ break;
+ default:
+ state->pd->pam_status = PAM_SYSTEM_ERR;
+ dp_err = DP_ERR_FATAL;
+ }
+
+ if (result == SDAP_UNAVAIL) {
+ be_mark_offline(state->breq->be_ctx);
+ dp_err = DP_ERR_OFFLINE;
+ goto done;
+ }
+
+ if (result == SDAP_AUTH_SUCCESS &&
+ state->breq->be_ctx->domain->cache_credentials) {
+
+ char *password = talloc_strndup(state, (char *)
+ state->password.data,
+ state->password.length);
+ /* password caching failures are not fatal errors */
+ if (!password) {
+ DEBUG(2, ("Failed to cache password for %s\n", state->username));
+ goto done;
+ }
+ talloc_set_destructor((TALLOC_CTX *)password, password_destructor);
+
+ subreq = sysdb_cache_password_send(state,
+ state->breq->be_ctx->ev,
+ state->breq->be_ctx->sysdb,
+ NULL,
+ state->breq->be_ctx->domain,
+ state->username, password);
+
+ /* password caching failures are not fatal errors */
+ if (!subreq) {
+ DEBUG(2, ("Failed to cache password for %s\n", state->username));
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sdap_password_cache_done, state);
+ return;
+ }
+
+done:
+ sdap_pam_auth_reply(state->breq, dp_err, state->pd->pam_status);
+}
+
+static void sdap_password_cache_done(struct tevent_req *subreq)
+{
+ struct sdap_pam_auth_state *state = tevent_req_callback_data(subreq,
+ struct sdap_pam_auth_state);
+ int ret;
+
+ ret = sysdb_cache_password_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ /* password caching failures are not fatal errors */
+ DEBUG(2, ("Failed to cache password for %s\n", state->username));
+ } else {
+ DEBUG(4, ("Password successfully cached for %s\n", state->username));
+ }
+
+ sdap_pam_auth_reply(state->breq, DP_ERR_OK, state->pd->pam_status);
+}
+
+static void sdap_pam_auth_reply(struct be_req *req, int dp_err, int result)
+{
+ req->fn(req, dp_err, result, NULL);
+}
+
diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
new file mode 100644
index 000000000..0d34be2ca
--- /dev/null
+++ b/src/providers/ldap/ldap_child.c
@@ -0,0 +1,429 @@
+/*
+ SSSD
+
+ LDAP Backend Module -- prime ccache with TGT in a child process
+
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ 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 <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <popt.h>
+
+#include <security/pam_modules.h>
+
+#include "util/util.h"
+#include "util/sss_krb5.h"
+#include "providers/child_common.h"
+#include "providers/dp_backend.h"
+
+static krb5_context krb5_error_ctx;
+
+struct input_buffer {
+ const char *realm_str;
+ const char *princ_str;
+ const char *keytab_name;
+};
+
+static errno_t unpack_buffer(uint8_t *buf, size_t size,
+ struct input_buffer *ibuf)
+{
+ size_t p = 0;
+ uint32_t len;
+
+ DEBUG(7, ("total buffer size: %d\n", size));
+
+ /* realm_str size and length */
+ COPY_UINT32_CHECK(&len, buf + p, p, size);
+
+ DEBUG(7, ("realm_str size: %d\n", len));
+ if (len) {
+ if ((p + len ) > size) return EINVAL;
+ ibuf->realm_str = talloc_strndup(ibuf, (char *)(buf + p), len);
+ DEBUG(7, ("got realm_str: %s\n", ibuf->realm_str));
+ if (ibuf->realm_str == NULL) return ENOMEM;
+ p += len;
+ }
+
+ /* princ_str size and length */
+ COPY_UINT32_CHECK(&len, buf + p, p, size);
+
+ DEBUG(7, ("princ_str size: %d\n", len));
+ if (len) {
+ if ((p + len ) > size) return EINVAL;
+ ibuf->princ_str = talloc_strndup(ibuf, (char *)(buf + p), len);
+ DEBUG(7, ("got princ_str: %s\n", ibuf->princ_str));
+ if (ibuf->princ_str == NULL) return ENOMEM;
+ p += len;
+ }
+
+ /* keytab_name size and length */
+ COPY_UINT32_CHECK(&len, buf + p, p, size);
+
+ DEBUG(7, ("keytab_name size: %d\n", len));
+ if (len) {
+ if ((p + len ) > size) return EINVAL;
+ ibuf->keytab_name = talloc_strndup(ibuf, (char *)(buf + p), len);
+ DEBUG(7, ("got keytab_name: %s\n", ibuf->keytab_name));
+ if (ibuf->keytab_name == NULL) return ENOMEM;
+ p += len;
+ }
+
+ return EOK;
+}
+
+static int pack_buffer(struct response *r, int result, const char *msg)
+{
+ int len;
+ int p = 0;
+
+ len = strlen(msg);
+ r->size = 2 * sizeof(uint32_t) + len;
+
+ /* result */
+ COPY_UINT32_VALUE(&r->buf[p], result, p);
+
+ /* message size */
+ COPY_UINT32_VALUE(&r->buf[p], len, p);
+
+ /* message itself */
+ COPY_MEM(&r->buf[p], msg, p, len);
+
+ return EOK;
+}
+
+static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
+ const char *realm_str,
+ const char *princ_str,
+ const char *keytab_name,
+ const char **ccname_out)
+{
+ char *ccname;
+ char *realm_name = NULL;
+ char *full_princ = NULL;
+ krb5_context context = NULL;
+ krb5_keytab keytab = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_principal kprinc;
+ krb5_creds my_creds;
+ krb5_get_init_creds_opt options;
+ krb5_error_code krberr;
+ int ret;
+
+ krberr = krb5_init_context(&context);
+ if (krberr) {
+ DEBUG(2, ("Failed to init kerberos context\n"));
+ return EFAULT;
+ }
+
+ if (!realm_str) {
+ krberr = krb5_get_default_realm(context, &realm_name);
+ if (krberr) {
+ DEBUG(2, ("Failed to get default realm name: %s\n",
+ sss_krb5_get_error_message(context, krberr)));
+ ret = EFAULT;
+ goto done;
+ }
+ } else {
+ realm_name = talloc_strdup(memctx, realm_str);
+ if (!realm_name) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (princ_str) {
+ if (!strchr(princ_str, '@')) {
+ full_princ = talloc_asprintf(memctx, "%s@%s",
+ princ_str, realm_name);
+ } else {
+ full_princ = talloc_strdup(memctx, princ_str);
+ }
+ } else {
+ char hostname[512];
+
+ ret = gethostname(hostname, 511);
+ if (ret == -1) {
+ ret = errno;
+ goto done;
+ }
+ hostname[511] = '\0';
+
+ full_princ = talloc_asprintf(memctx, "host/%s@%s",
+ hostname, realm_name);
+ }
+ if (!full_princ) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(4, ("Principal name is: [%s]\n", full_princ));
+
+ krberr = krb5_parse_name(context, full_princ, &kprinc);
+ if (krberr) {
+ DEBUG(2, ("Unable to build principal: %s\n",
+ sss_krb5_get_error_message(context, krberr)));
+ ret = EFAULT;
+ goto done;
+ }
+
+ if (keytab_name) {
+ krberr = krb5_kt_resolve(context, keytab_name, &keytab);
+ } else {
+ krberr = krb5_kt_default(context, &keytab);
+ }
+ if (krberr) {
+ DEBUG(2, ("Failed to read keytab file: %s\n",
+ sss_krb5_get_error_message(context, krberr)));
+ ret = EFAULT;
+ goto done;
+ }
+
+ ccname = talloc_asprintf(memctx, "FILE:%s/ccache_%s", DB_PATH, realm_name);
+ if (!ccname) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ krberr = krb5_cc_resolve(context, ccname, &ccache);
+ if (krberr) {
+ DEBUG(2, ("Failed to set cache name: %s\n",
+ sss_krb5_get_error_message(context, krberr)));
+ ret = EFAULT;
+ goto done;
+ }
+
+ memset(&my_creds, 0, sizeof(my_creds));
+ memset(&options, 0, sizeof(options));
+
+ krb5_get_init_creds_opt_set_address_list(&options, NULL);
+ krb5_get_init_creds_opt_set_forwardable(&options, 0);
+ krb5_get_init_creds_opt_set_proxiable(&options, 0);
+ /* set a very short lifetime, we don't keep the ticket around */
+ krb5_get_init_creds_opt_set_tkt_life(&options, 300);
+
+ krberr = krb5_get_init_creds_keytab(context, &my_creds, kprinc,
+ keytab, 0, NULL, &options);
+
+ if (krberr) {
+ DEBUG(2, ("Failed to init credentials: %s\n",
+ sss_krb5_get_error_message(context, krberr)));
+ ret = EFAULT;
+ goto done;
+ }
+
+ krberr = krb5_cc_initialize(context, ccache, kprinc);
+ if (krberr) {
+ DEBUG(2, ("Failed to init ccache: %s\n",
+ sss_krb5_get_error_message(context, krberr)));
+ ret = EFAULT;
+ goto done;
+ }
+
+ krberr = krb5_cc_store_cred(context, ccache, &my_creds);
+ if (krberr) {
+ DEBUG(2, ("Failed to store creds: %s\n",
+ sss_krb5_get_error_message(context, krberr)));
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EOK;
+ *ccname_out = ccname;
+
+done:
+ if (keytab) krb5_kt_close(context, keytab);
+ if (context) krb5_free_context(context);
+ return ret;
+}
+
+static int prepare_response(TALLOC_CTX *mem_ctx,
+ const char *ccname,
+ krb5_error_code kerr,
+ struct response **rsp)
+{
+ int ret;
+ struct response *r = NULL;
+ const char *krb5_msg = NULL;
+
+ r = talloc_zero(mem_ctx, struct response);
+ if (!r) return ENOMEM;
+
+ r->buf = talloc_size(mem_ctx, MAX_CHILD_MSG_SIZE);
+ if (r->buf == NULL) {
+ DEBUG(1, ("talloc_size failed.\n"));
+ return ENOMEM;
+ }
+ r->max_size = MAX_CHILD_MSG_SIZE;
+ r->size = 0;
+
+ if (kerr == 0) {
+ ret = pack_buffer(r, EOK, ccname);
+ } else {
+ krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr);
+ if (krb5_msg == NULL) {
+ DEBUG(1, ("sss_krb5_get_error_message failed.\n"));
+ return ENOMEM;
+ }
+
+ ret = pack_buffer(r, EFAULT, krb5_msg);
+ sss_krb5_free_error_message(krb5_error_ctx, krb5_msg);
+ }
+
+ if (ret != EOK) {
+ DEBUG(1, ("pack_buffer failed\n"));
+ return ret;
+ }
+
+ *rsp = r;
+ return EOK;
+}
+
+int main(int argc, const char *argv[])
+{
+ int ret;
+ int kerr;
+ int opt;
+ int debug_fd = -1;
+ poptContext pc;
+ TALLOC_CTX *main_ctx;
+ uint8_t *buf = NULL;
+ ssize_t len = 0;
+ const char *ccname = NULL;
+ struct input_buffer *ibuf = NULL;
+ struct response *resp = NULL;
+ size_t written;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0,
+ _("Debug level"), NULL},
+ {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0,
+ _("Add debug timestamps"), NULL},
+ {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
+ _("An open file descriptor for the debug logs"), NULL},
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ _exit(-1);
+ }
+ }
+
+ poptFreeContext(pc);
+
+ DEBUG(7, ("ldap_child started.\n"));
+
+ main_ctx = talloc_new(NULL);
+ if (main_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed.\n"));
+ _exit(-1);
+ }
+
+ debug_prg_name = talloc_asprintf(main_ctx, "[sssd[ldap_child[%d]]]", getpid());
+
+ if (debug_fd != -1) {
+ ret = set_debug_file_from_fd(debug_fd);
+ if (ret != EOK) {
+ DEBUG(1, ("set_debug_file_from_fd failed.\n"));
+ }
+ }
+
+ buf = talloc_size(main_ctx, sizeof(uint8_t)*IN_BUF_SIZE);
+ if (buf == NULL) {
+ DEBUG(1, ("talloc_size failed.\n"));
+ goto fail;
+ }
+
+ ibuf = talloc_zero(main_ctx, struct input_buffer);
+ if (ibuf == NULL) {
+ DEBUG(1, ("talloc_size failed.\n"));
+ goto fail;
+ }
+
+ while ((ret = read(STDIN_FILENO, buf + len, IN_BUF_SIZE - len)) != 0) {
+ if (ret == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno)));
+ goto fail;
+ } else if (ret > 0) {
+ len += ret;
+ if (len > IN_BUF_SIZE) {
+ DEBUG(1, ("read too much, this should never happen.\n"));
+ goto fail;
+ }
+ continue;
+ } else {
+ DEBUG(1, ("unexpected return code of read [%d].\n", ret));
+ goto fail;
+ }
+ }
+ close(STDIN_FILENO);
+
+ ret = unpack_buffer(buf, len, ibuf);
+ if (ret != EOK) {
+ DEBUG(1, ("unpack_buffer failed.[%d][%s].\n", ret, strerror(ret)));
+ goto fail;
+ }
+
+ kerr = ldap_child_get_tgt_sync(main_ctx,
+ ibuf->realm_str, ibuf->princ_str,
+ ibuf->keytab_name, &ccname);
+ if (kerr != EOK) {
+ DEBUG(1, ("ldap_child_get_tgt_sync failed.\n"));
+ /* Do not return, must report failure */
+ }
+
+ ret = prepare_response(main_ctx, ccname, kerr, &resp);
+ if (ret != EOK) {
+ DEBUG(1, ("prepare_response failed. [%d][%s].\n", ret, strerror(ret)));
+ return ENOMEM;
+ }
+
+ written = 0;
+ while (written < resp->size) {
+ ret = write(STDOUT_FILENO, resp->buf + written, resp->size - written);
+ if (ret == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ continue;
+ }
+ ret = errno;
+ DEBUG(1, ("write failed [%d][%s].\n", ret, strerror(ret)));
+ return ret;
+ }
+ written += ret;
+ }
+
+ close(STDOUT_FILENO);
+ talloc_free(main_ctx);
+ _exit(0);
+
+fail:
+ close(STDOUT_FILENO);
+ talloc_free(main_ctx);
+ _exit(-1);
+}
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
new file mode 100644
index 000000000..bd4294f83
--- /dev/null
+++ b/src/providers/ldap/ldap_common.c
@@ -0,0 +1,589 @@
+/*
+ SSSD
+
+ LDAP Provider Common Functions
+
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+
+ Copyright (C) 2008-2009 Red Hat
+
+ 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 "providers/ldap/ldap_common.h"
+#include "providers/fail_over.h"
+
+#include "util/sss_krb5.h"
+
+/* a fd the child process would log into */
+int ldap_child_debug_fd = -1;
+
+struct dp_option default_basic_opts[] = {
+ { "ldap_uri", DP_OPT_STRING, { "ldap://localhost" }, NULL_STRING },
+ { "ldap_search_base", DP_OPT_STRING, { "dc=example,dc=com" }, NULL_STRING },
+ { "ldap_default_bind_dn", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_default_authtok_type", DP_OPT_STRING, NULL_STRING, NULL_STRING},
+ { "ldap_default_authtok", DP_OPT_BLOB, NULL_BLOB, NULL_BLOB },
+ { "ldap_search_timeout", DP_OPT_NUMBER, { .number = 60 }, NULL_NUMBER },
+ { "ldap_network_timeout", DP_OPT_NUMBER, { .number = 6 }, NULL_NUMBER },
+ { "ldap_opt_timeout", DP_OPT_NUMBER, { .number = 6 }, NULL_NUMBER },
+ { "ldap_tls_reqcert", DP_OPT_STRING, { "hard" }, NULL_STRING },
+ { "ldap_user_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_user_search_scope", DP_OPT_STRING, { "sub" }, NULL_STRING },
+ { "ldap_user_search_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_group_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_group_search_scope", DP_OPT_STRING, { "sub" }, NULL_STRING },
+ { "ldap_group_search_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_schema", DP_OPT_STRING, { "rfc2307" }, NULL_STRING },
+ { "ldap_offline_timeout", DP_OPT_NUMBER, { .number = 60 }, NULL_NUMBER },
+ { "ldap_force_upper_case_realm", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+ { "ldap_enumeration_refresh_timeout", DP_OPT_NUMBER, { .number = 300 }, NULL_NUMBER },
+ { "ldap_purge_cache_timeout", DP_OPT_NUMBER, { .number = 3600 }, NULL_NUMBER },
+ { "entry_cache_timoeut", DP_OPT_NUMBER, { .number = 1800 }, NULL_NUMBER },
+ { "ldap_tls_cacert", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_tls_cacertdir", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_id_use_start_tls", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+ { "ldap_sasl_mech", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_sasl_authid", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_krb5_keytab", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_krb5_init_creds", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+ /* use the same parm name as the krb5 module so we set it only once */
+ { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_pwd_policy", DP_OPT_STRING, { "none" } , NULL_STRING },
+ { "ldap_referrals", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }
+};
+
+struct sdap_attr_map generic_attr_map[] = {
+ { "ldap_entry_usn", NULL, SYSDB_USN, NULL },
+ { "ldap_rootdse_last_usn", NULL, SYSDB_USN, NULL }
+};
+
+struct sdap_attr_map gen_ipa_attr_map[] = {
+ { "ldap_entry_usn", "entryUSN", SYSDB_USN, NULL },
+ { "ldap_rootdse_last_usn", "lastUSN", SYSDB_HIGH_USN, NULL }
+};
+
+struct sdap_attr_map gen_ad_attr_map[] = {
+ { "ldap_entry_usn", "uSNChanged", SYSDB_USN, NULL },
+ { "ldap_rootdse_last_usn", "highestCommittedUSN", SYSDB_HIGH_USN, NULL }
+};
+
+struct sdap_attr_map rfc2307_user_map[] = {
+ { "ldap_user_object_class", "posixAccount", SYSDB_USER_CLASS, NULL },
+ { "ldap_user_name", "uid", SYSDB_NAME, NULL },
+ { "ldap_user_pwd", "userPassword", SYSDB_PWD, NULL },
+ { "ldap_user_uid_number", "uidNumber", SYSDB_UIDNUM, NULL },
+ { "ldap_user_gid_number", "gidNumber", SYSDB_GIDNUM, NULL },
+ { "ldap_user_gecos", "gecos", SYSDB_GECOS, NULL },
+ { "ldap_user_home_directory", "homeDirectory", SYSDB_HOMEDIR, NULL },
+ { "ldap_user_shell", "loginShell", SYSDB_SHELL, NULL },
+ { "ldap_user_principal", "krbPrincipalName", SYSDB_UPN, NULL },
+ { "ldap_user_fullname", "cn", SYSDB_FULLNAME, NULL },
+ { "ldap_user_member_of", NULL, SYSDB_MEMBEROF, NULL },
+ { "ldap_user_uuid", NULL, SYSDB_UUID, NULL },
+ { "ldap_user_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+ { "ldap_user_shadow_last_change", "shadowLastChange", SYSDB_SHADOWPW_LASTCHANGE, NULL },
+ { "ldap_user_shadow_min", "shadowMin", SYSDB_SHADOWPW_MIN, NULL },
+ { "ldap_user_shadow_max", "shadowMax", SYSDB_SHADOWPW_MAX, NULL },
+ { "ldap_user_shadow_warning", "shadowWarning", SYSDB_SHADOWPW_WARNING, NULL },
+ { "ldap_user_shadow_inactive", "shadowInactive", SYSDB_SHADOWPW_INACTIVE, NULL },
+ { "ldap_user_shadow_expire", "shadowExpire", SYSDB_SHADOWPW_EXPIRE, NULL },
+ { "ldap_user_shadow_flag", "shadowFlag", SYSDB_SHADOWPW_FLAG, NULL },
+ { "ldap_user_krb_last_pwd_change", "krbLastPwdChange", SYSDB_KRBPW_LASTCHANGE, NULL },
+ { "ldap_user_krb_password_expiration", "krbPasswordExpiration", SYSDB_KRBPW_EXPIRATION, NULL },
+ { "ldap_pwd_attribute", "pwdAttribute", SYSDB_PWD_ATTRIBUTE, NULL }
+};
+
+struct sdap_attr_map rfc2307_group_map[] = {
+ { "ldap_group_object_class", "posixGroup", SYSDB_GROUP_CLASS, NULL },
+ { "ldap_group_name", "cn", SYSDB_NAME, NULL },
+ { "ldap_group_pwd", "userPassword", SYSDB_PWD, NULL },
+ { "ldap_group_gid_number", "gidNumber", SYSDB_GIDNUM, NULL },
+ { "ldap_group_member", "memberuid", SYSDB_MEMBER, NULL },
+ { "ldap_group_uuid", NULL, SYSDB_UUID, NULL },
+ { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
+};
+
+struct sdap_attr_map rfc2307bis_user_map[] = {
+ { "ldap_user_object_class", "posixAccount", SYSDB_USER_CLASS, NULL },
+ { "ldap_user_name", "uid", SYSDB_NAME, NULL },
+ { "ldap_user_pwd", "userPassword", SYSDB_PWD, NULL },
+ { "ldap_user_uid_number", "uidNumber", SYSDB_UIDNUM, NULL },
+ { "ldap_user_gid_number", "gidNumber", SYSDB_GIDNUM, NULL },
+ { "ldap_user_gecos", "gecos", SYSDB_GECOS, NULL },
+ { "ldap_user_home_directory", "homeDirectory", SYSDB_HOMEDIR, NULL },
+ { "ldap_user_shell", "loginShell", SYSDB_SHELL, NULL },
+ { "ldap_user_principal", "krbPrincipalName", SYSDB_UPN, NULL },
+ { "ldap_user_fullname", "cn", SYSDB_FULLNAME, NULL },
+ { "ldap_user_member_of", "memberOf", SYSDB_MEMBEROF, NULL },
+ /* FIXME: this is 389ds specific */
+ { "ldap_user_uuid", "nsUniqueId", SYSDB_UUID, NULL },
+ { "ldap_user_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+ { "ldap_user_shadow_last_change", "shadowLastChange", SYSDB_SHADOWPW_LASTCHANGE, NULL },
+ { "ldap_user_shadow_min", "shadowMin", SYSDB_SHADOWPW_MIN, NULL },
+ { "ldap_user_shadow_max", "shadowMax", SYSDB_SHADOWPW_MAX, NULL },
+ { "ldap_user_shadow_warning", "shadowWarning", SYSDB_SHADOWPW_WARNING, NULL },
+ { "ldap_user_shadow_inactive", "shadowInactive", SYSDB_SHADOWPW_INACTIVE, NULL },
+ { "ldap_user_shadow_expire", "shadowExpire", SYSDB_SHADOWPW_EXPIRE, NULL },
+ { "ldap_user_shadow_flag", "shadowFlag", SYSDB_SHADOWPW_FLAG, NULL },
+ { "ldap_user_krb_last_pwd_change", "krbLastPwdChange", SYSDB_KRBPW_LASTCHANGE, NULL },
+ { "ldap_user_krb_password_expiration", "krbPasswordExpiration", SYSDB_KRBPW_EXPIRATION, NULL },
+ { "ldap_pwd_attribute", "pwdAttribute", SYSDB_PWD_ATTRIBUTE, NULL }
+};
+
+struct sdap_attr_map rfc2307bis_group_map[] = {
+ { "ldap_group_object_class", "posixGroup", SYSDB_GROUP_CLASS, NULL },
+ { "ldap_group_name", "cn", SYSDB_NAME, NULL },
+ { "ldap_group_pwd", "userPassword", SYSDB_PWD, NULL },
+ { "ldap_group_gid_number", "gidNumber", SYSDB_GIDNUM, NULL },
+ { "ldap_group_member", "member", SYSDB_MEMBER, NULL },
+ /* FIXME: this is 389ds specific */
+ { "ldap_group_uuid", "nsUniqueId", SYSDB_UUID, NULL },
+ { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
+};
+
+int ldap_get_options(TALLOC_CTX *memctx,
+ struct confdb_ctx *cdb,
+ const char *conf_path,
+ struct sdap_options **_opts)
+{
+ struct sdap_attr_map *default_attr_map;
+ struct sdap_attr_map *default_user_map;
+ struct sdap_attr_map *default_group_map;
+ struct sdap_options *opts;
+ char *schema;
+ const char *pwd_policy;
+ int ret;
+
+ opts = talloc_zero(memctx, struct sdap_options);
+ if (!opts) return ENOMEM;
+
+ ret = dp_get_options(opts, cdb, conf_path,
+ default_basic_opts,
+ SDAP_OPTS_BASIC,
+ &opts->basic);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* set user/group search bases if they are not */
+ if (NULL == dp_opt_get_string(opts->basic, SDAP_USER_SEARCH_BASE)) {
+ ret = dp_opt_set_string(opts->basic, SDAP_USER_SEARCH_BASE,
+ dp_opt_get_string(opts->basic,
+ SDAP_SEARCH_BASE));
+ if (ret != EOK) {
+ goto done;
+ }
+ DEBUG(6, ("Option %s set to %s\n",
+ opts->basic[SDAP_USER_SEARCH_BASE].opt_name,
+ dp_opt_get_string(opts->basic, SDAP_USER_SEARCH_BASE)));
+ }
+
+ if (NULL == dp_opt_get_string(opts->basic, SDAP_GROUP_SEARCH_BASE)) {
+ ret = dp_opt_set_string(opts->basic, SDAP_GROUP_SEARCH_BASE,
+ dp_opt_get_string(opts->basic,
+ SDAP_SEARCH_BASE));
+ if (ret != EOK) {
+ goto done;
+ }
+ DEBUG(6, ("Option %s set to %s\n",
+ opts->basic[SDAP_GROUP_SEARCH_BASE].opt_name,
+ dp_opt_get_string(opts->basic, SDAP_GROUP_SEARCH_BASE)));
+ }
+
+ pwd_policy = dp_opt_get_string(opts->basic, SDAP_PWD_POLICY);
+ if (pwd_policy == NULL) {
+ DEBUG(1, ("Missing password policy, this may not happen.\n"));
+ ret = EINVAL;
+ goto done;
+ }
+ if (strcasecmp(pwd_policy, PWD_POL_OPT_NONE) != 0 &&
+ strcasecmp(pwd_policy, PWD_POL_OPT_SHADOW) != 0 &&
+ strcasecmp(pwd_policy, PWD_POL_OPT_MIT) != 0) {
+ DEBUG(1, ("Unsupported password policy [%s].\n", pwd_policy));
+ ret = EINVAL;
+ goto done;
+ }
+
+
+#ifndef HAVE_LDAP_CONNCB
+ bool ldap_referrals;
+
+ ldap_referrals = dp_opt_get_bool(opts->basic, SDAP_REFERRALS);
+ if (ldap_referrals) {
+ DEBUG(1, ("LDAP referrals are not supported, because the LDAP library "
+ "is too old, see sssd-ldap(5) for details.\n"));
+ ret = dp_opt_set_bool(opts->basic, SDAP_REFERRALS, false);
+ }
+#endif
+
+ /* schema type */
+ schema = dp_opt_get_string(opts->basic, SDAP_SCHEMA);
+ if (strcasecmp(schema, "rfc2307") == 0) {
+ opts->schema_type = SDAP_SCHEMA_RFC2307;
+ default_attr_map = generic_attr_map;
+ default_user_map = rfc2307_user_map;
+ default_group_map = rfc2307_group_map;
+ } else
+ if (strcasecmp(schema, "rfc2307bis") == 0) {
+ opts->schema_type = SDAP_SCHEMA_RFC2307BIS;
+ default_attr_map = generic_attr_map;
+ default_user_map = rfc2307bis_user_map;
+ default_group_map = rfc2307bis_group_map;
+ } else
+ if (strcasecmp(schema, "IPA") == 0) {
+ opts->schema_type = SDAP_SCHEMA_IPA_V1;
+ default_attr_map = gen_ipa_attr_map;
+ default_user_map = rfc2307bis_user_map;
+ default_group_map = rfc2307bis_group_map;
+ } else
+ if (strcasecmp(schema, "AD") == 0) {
+ opts->schema_type = SDAP_SCHEMA_AD;
+ default_attr_map = gen_ad_attr_map;
+ default_user_map = rfc2307bis_user_map;
+ default_group_map = rfc2307bis_group_map;
+ } else {
+ DEBUG(0, ("Unrecognized schema type: %s\n", schema));
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sdap_get_map(opts, cdb, conf_path,
+ default_attr_map,
+ SDAP_AT_GENERAL,
+ &opts->gen_map);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sdap_get_map(opts, cdb, conf_path,
+ default_user_map,
+ SDAP_OPTS_USER,
+ &opts->user_map);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sdap_get_map(opts, cdb, conf_path,
+ default_group_map,
+ SDAP_OPTS_GROUP,
+ &opts->group_map);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+ *_opts = opts;
+
+done:
+ if (ret != EOK) {
+ talloc_zfree(opts);
+ }
+ return ret;
+}
+
+void sdap_handler_done(struct be_req *req, int dp_err,
+ int error, const char *errstr)
+{
+ return req->fn(req, dp_err, error, errstr);
+}
+
+bool sdap_connected(struct sdap_id_ctx *ctx)
+{
+ if (ctx->gsh) {
+ return ctx->gsh->connected;
+ }
+
+ return false;
+}
+
+void sdap_mark_offline(struct sdap_id_ctx *ctx)
+{
+ if (ctx->gsh) {
+ /* make sure we mark the connection as gone when we go offline so that
+ * we do not try to reuse a bad connection by mistale later */
+ talloc_zfree(ctx->gsh);
+ }
+
+ be_mark_offline(ctx->be);
+}
+
+bool sdap_check_gssapi_reconnect(struct sdap_id_ctx *ctx)
+{
+ int ret;
+ bool result = false;
+ const char *mech;
+ const char *realm;
+ char *ccname = NULL;
+ krb5_context context = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_error_code krberr;
+ TALLOC_CTX *tmp_ctx = NULL;
+ krb5_creds mcred;
+ krb5_creds cred;
+ char *server_name = NULL;
+ char *client_princ_str = NULL;
+ char *full_princ = NULL;
+ krb5_principal client_principal = NULL;
+ krb5_principal server_principal = NULL;
+ char hostname[512];
+ int l_errno;
+
+
+ mech = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_MECH);
+ if (mech == NULL || strcasecmp(mech, "GSSAPI") != 0) {
+ return false;
+ }
+
+ realm = dp_opt_get_string(ctx->opts->basic, SDAP_KRB5_REALM);
+ if (realm == NULL) {
+ DEBUG(3, ("Kerberos realm not available.\n"));
+ return false;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed.\n"));
+ return false;
+ }
+
+ ccname = talloc_asprintf(tmp_ctx, "FILE:%s/ccache_%s", DB_PATH, realm);
+ if (ccname == NULL) {
+ DEBUG(1, ("talloc_asprintf failed.\n"));
+ goto done;
+ }
+
+ krberr = krb5_init_context(&context);
+ if (krberr) {
+ DEBUG(1, ("Failed to init kerberos context\n"));
+ goto done;
+ }
+
+ krberr = krb5_cc_resolve(context, ccname, &ccache);
+ if (krberr != 0) {
+ DEBUG(1, ("krb5_cc_resolve failed.\n"));
+ goto done;
+ }
+
+ server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
+ if (server_name == NULL) {
+ DEBUG(1, ("talloc_asprintf failed.\n"));
+ goto done;
+ }
+
+ krberr = krb5_parse_name(context, server_name, &server_principal);
+ if (krberr != 0) {
+ DEBUG(1, ("krb5_parse_name failed.\n"));
+ goto done;
+ }
+
+ client_princ_str = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_AUTHID);
+ if (client_princ_str) {
+ if (!strchr(client_princ_str, '@')) {
+ full_princ = talloc_asprintf(tmp_ctx, "%s@%s", client_princ_str,
+ realm);
+ } else {
+ full_princ = talloc_strdup(tmp_ctx, client_princ_str);
+ }
+ } else {
+ ret = gethostname(hostname, sizeof(hostname)-1);
+ if (ret == -1) {
+ l_errno = errno;
+ DEBUG(1, ("gethostname failed [%d][%s].\n", l_errno,
+ strerror(l_errno)));
+ goto done;
+ }
+ hostname[sizeof(hostname)-1] = '\0';
+
+ full_princ = talloc_asprintf(tmp_ctx, "host/%s@%s", hostname, realm);
+ }
+ if (!full_princ) {
+ DEBUG(1, ("Client principal not available.\n"));
+ goto done;
+ }
+ DEBUG(7, ("Client principal name is: [%s]\n", full_princ));
+ krberr = krb5_parse_name(context, full_princ, &client_principal);
+ if (krberr != 0) {
+ DEBUG(1, ("krb5_parse_name failed.\n"));
+ goto done;
+ }
+
+ memset(&mcred, 0, sizeof(mcred));
+ memset(&cred, 0, sizeof(mcred));
+ mcred.client = client_principal;
+ mcred.server = server_principal;
+
+ krberr = krb5_cc_retrieve_cred(context, ccache, 0, &mcred, &cred);
+ if (krberr != 0) {
+ DEBUG(1, ("krb5_cc_retrieve_cred failed.\n"));
+ goto done;
+ }
+
+ DEBUG(7, ("TGT end time [%d].\n", cred.times.endtime));
+
+ if (cred.times.endtime <= time(NULL)) {
+ DEBUG(3, ("TGT is expired.\n"));
+ result = true;
+ }
+ krb5_free_cred_contents(context, &cred);
+
+done:
+ if (client_principal != NULL) {
+ krb5_free_principal(context, client_principal);
+ }
+ if (server_principal != NULL) {
+ krb5_free_principal(context, server_principal);
+ }
+ if (ccache != NULL) {
+ if (result) {
+ krb5_cc_destroy(context, ccache);
+ } else {
+ krb5_cc_close(context, ccache);
+ }
+ }
+ if (context != NULL) krb5_free_context(context);
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+int sdap_id_setup_tasks(struct sdap_id_ctx *ctx)
+{
+ struct timeval tv;
+ int ret = EOK;
+
+ /* set up enumeration task */
+ if (ctx->be->domain->enumerate) {
+ /* run the first one in a couple of seconds so that we have time to
+ * finish initializations first*/
+ tv = tevent_timeval_current_ofs(10, 0);
+ ret = ldap_id_enumerate_set_timer(ctx, tv);
+ } else {
+ /* the enumeration task, runs the cleanup process by itself,
+ * but if enumeration is not runnig we need to schedule it */
+
+ /* run the first one in a couple of seconds so that we have time to
+ * finish initializations first*/
+ tv = tevent_timeval_current_ofs(10, 0);
+ ret = ldap_id_cleanup_set_timer(ctx, tv);
+ }
+
+ return ret;
+}
+
+static void sdap_uri_callback(void *private_data, struct fo_server *server)
+{
+ struct sdap_service *service;
+ const char *tmp;
+ char *new_uri;
+
+ service = talloc_get_type(private_data, struct sdap_service);
+ if (!service) return;
+
+ tmp = (const char *)fo_get_server_user_data(server);
+ if (tmp && ldap_is_ldap_url(tmp)) {
+ new_uri = talloc_strdup(service, tmp);
+ } else {
+ new_uri = talloc_asprintf(service, "ldap://%s",
+ fo_get_server_name(server));
+ }
+ if (!new_uri) {
+ DEBUG(2, ("Failed to copy URI ...\n"));
+ return;
+ }
+
+ /* free old one and replace with new one */
+ talloc_zfree(service->uri);
+ service->uri = new_uri;
+}
+
+int sdap_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
+ const char *service_name, const char *urls,
+ struct sdap_service **_service)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sdap_service *service;
+ LDAPURLDesc *lud;
+ char **list = NULL;
+ int ret;
+ int i;
+
+ tmp_ctx = talloc_new(memctx);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ service = talloc_zero(tmp_ctx, struct sdap_service);
+ if (!service) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = be_fo_add_service(ctx, service_name);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to create failover service!\n"));
+ goto done;
+ }
+
+ service->name = talloc_strdup(service, service_name);
+ if (!service->name) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* split server parm into a list */
+ ret = split_on_separator(tmp_ctx, urls, ',', true, &list, NULL);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to parse server list!\n"));
+ goto done;
+ }
+
+ /* now for each URI add a new server to the failover service */
+ for (i = 0; list[i]; i++) {
+ ret = ldap_url_parse(list[i], &lud);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(0, ("Failed to parse ldap URI (%s)!\n", list[i]));
+ ret = EINVAL;
+ goto done;
+ }
+
+ DEBUG(6, ("Added URI %s\n", list[i]));
+
+ talloc_steal(service, list[i]);
+
+ ret = be_fo_add_server(ctx, service->name,
+ lud->lud_host, lud->lud_port, list[i]);
+ if (ret) {
+ goto done;
+ }
+ ldap_free_urldesc(lud);
+ }
+
+ ret = be_fo_service_add_callback(memctx, ctx, service->name,
+ sdap_uri_callback, service);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to add failover callback!\n"));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_service = talloc_steal(memctx, service);
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
new file mode 100644
index 000000000..ff1ffb725
--- /dev/null
+++ b/src/providers/ldap/ldap_common.h
@@ -0,0 +1,115 @@
+/*
+ SSSD
+
+ LDAP Common utility code
+
+ Copyright (C) Simo Sorce <ssorce@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/>.
+*/
+
+#ifndef _LDAP_COMMON_H_
+#define _LDAP_COMMON_H_
+
+#include "providers/dp_backend.h"
+#include "providers/ldap/sdap.h"
+#include "providers/fail_over.h"
+
+#define PWD_POL_OPT_NONE "none"
+#define PWD_POL_OPT_SHADOW "shadow"
+#define PWD_POL_OPT_MIT "mit_kerberos"
+
+/* a fd the child process would log into */
+extern int ldap_child_debug_fd;
+
+struct sdap_id_ctx {
+ struct be_ctx *be;
+ struct sdap_options *opts;
+ struct fo_service *fo_service;
+ struct sdap_service *service;
+
+ /* what rootDSE returns */
+ struct sysdb_attrs *rootDSE;
+
+ /* global sdap handler */
+ struct sdap_handle *gsh;
+
+ /* enumeration loop timer */
+ struct timeval last_enum;
+ /* cleanup loop timer */
+ struct timeval last_purge;
+
+ char *max_user_timestamp;
+ char *max_group_timestamp;
+};
+
+struct sdap_auth_ctx {
+ struct be_ctx *be;
+ struct sdap_options *opts;
+ struct fo_service *fo_service;
+ struct sdap_service *service;
+};
+
+/* id */
+void sdap_account_info_handler(struct be_req *breq);
+int sdap_id_setup_tasks(struct sdap_id_ctx *ctx);
+
+/* auth */
+void sdap_pam_auth_handler(struct be_req *breq);
+
+/* chpass */
+void sdap_pam_chpass_handler(struct be_req *breq);
+
+
+
+void sdap_handler_done(struct be_req *req, int dp_err,
+ int error, const char *errstr);
+
+int sdap_service_init(TALLOC_CTX *mmectx, struct be_ctx *ctx,
+ const char *service_name, const char *urls,
+ struct sdap_service **service);
+
+/* options parser */
+int ldap_get_options(TALLOC_CTX *memctx,
+ struct confdb_ctx *cdb,
+ const char *conf_path,
+ struct sdap_options **_opts);
+
+int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
+int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
+
+bool sdap_connected(struct sdap_id_ctx *ctx);
+void sdap_mark_offline(struct sdap_id_ctx *ctx);
+bool sdap_check_gssapi_reconnect(struct sdap_id_ctx *ctx);
+
+struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ const char *name,
+ int filter_type,
+ int attrs_type);
+int users_get_recv(struct tevent_req *req);
+
+struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ const char *name,
+ int filter_type,
+ int attrs_type);
+int groups_get_recv(struct tevent_req *req);
+
+/* setup child logging */
+int setup_child(struct sdap_id_ctx *ctx);
+
+#endif /* _LDAP_COMMON_H_ */
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
new file mode 100644
index 000000000..4bbc07a68
--- /dev/null
+++ b/src/providers/ldap/ldap_id.c
@@ -0,0 +1,795 @@
+/*
+ SSSD
+
+ LDAP Identity Backend Module
+
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+
+ Copyright (C) 2008 Red Hat
+
+ 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 <time.h>
+#include <sys/time.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async.h"
+
+/* =Users-Related-Functions-(by-name,by-uid)============================== */
+
+struct users_get_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+
+ const char *name;
+ int filter_type;
+
+ char *filter;
+ const char **attrs;
+};
+
+static void users_get_connect_done(struct tevent_req *subreq);
+static void users_get_done(struct tevent_req *subreq);
+static void users_get_delete(struct tevent_req *subreq);
+
+struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ const char *name,
+ int filter_type,
+ int attrs_type)
+{
+ struct tevent_req *req, *subreq;
+ struct users_get_state *state;
+ const char *attr_name;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct users_get_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = ctx->be->sysdb;
+ state->domain = state->ctx->be->domain;
+ state->name = name;
+ state->filter_type = filter_type;
+
+ switch (filter_type) {
+ case BE_FILTER_NAME:
+ attr_name = ctx->opts->user_map[SDAP_AT_USER_NAME].name;
+ break;
+ case BE_FILTER_IDNUM:
+ attr_name = ctx->opts->user_map[SDAP_AT_USER_UID].name;
+ break;
+ default:
+ ret = EINVAL;
+ goto fail;
+ }
+
+ state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
+ attr_name, name,
+ ctx->opts->user_map[SDAP_OC_USER].name);
+ if (!state->filter) {
+ DEBUG(2, ("Failed to build filter\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ /* TODO: handle attrs_type */
+ ret = build_attrs_from_map(state, ctx->opts->user_map,
+ SDAP_OPTS_USER, &state->attrs);
+ if (ret != EOK) goto fail;
+
+ if (!sdap_connected(ctx)) {
+
+ if (ctx->gsh) talloc_zfree(ctx->gsh);
+
+ /* FIXME: add option to decide if tls should be used
+ * or SASL/GSSAPI, etc ... */
+ subreq = sdap_cli_connect_send(state, ev, ctx->opts,
+ ctx->be, ctx->service,
+ &ctx->rootDSE);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, users_get_connect_done, req);
+
+ return req;
+ }
+
+ subreq = sdap_get_users_send(state, state->ev,
+ state->domain, state->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->attrs, state->filter);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, users_get_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void users_get_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct users_get_state *state = tevent_req_data(req,
+ struct users_get_state);
+ int ret;
+
+ ret = sdap_cli_connect_recv(subreq, state->ctx,
+ &state->ctx->gsh, &state->ctx->rootDSE);
+ talloc_zfree(subreq);
+ if (ret) {
+ if (ret == ENOTSUP) {
+ DEBUG(0, ("Authentication mechanism not Supported by server"));
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_get_users_send(state, state->ev,
+ state->domain, state->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->attrs, state->filter);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, users_get_done, req);
+}
+
+static void users_get_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct users_get_state *state = tevent_req_data(req,
+ struct users_get_state);
+ char *endptr;
+ uid_t uid;
+ int ret;
+
+ ret = sdap_get_users_recv(subreq, NULL, NULL);
+ talloc_zfree(subreq);
+ if (ret && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (ret == ENOENT) {
+ if (strchr(state->name, '*')) {
+ /* it was an enumeration */
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ switch (state->filter_type) {
+ case BE_FILTER_NAME:
+ subreq = sysdb_delete_user_send(state, state->ev,
+ state->sysdb, NULL,
+ state->domain, state->name, 0);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, users_get_delete, req);
+ return;
+
+ case BE_FILTER_IDNUM:
+ errno = 0;
+ uid = (uid_t)strtol(state->name, &endptr, 0);
+ if (errno || *endptr || (state->name == endptr)) {
+ tevent_req_error(req, errno);
+ return;
+ }
+
+ subreq = sysdb_delete_user_send(state, state->ev,
+ state->sysdb, NULL,
+ state->domain, NULL, uid);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, users_get_delete, req);
+ return;
+
+ default:
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+static void users_get_delete(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct users_get_state *state = tevent_req_data(req,
+ struct users_get_state);
+ int ret;
+
+ ret = sysdb_delete_user_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("User (%s) delete returned %d (%s)\n",
+ state->name, ret, strerror(ret)));
+ }
+
+ tevent_req_done(req);
+}
+
+int users_get_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+/* =Groups-Related-Functions-(by-name,by-uid)============================= */
+
+struct groups_get_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+
+ const char *name;
+ int filter_type;
+
+ char *filter;
+ const char **attrs;
+};
+
+static void groups_get_connect_done(struct tevent_req *subreq);
+static void groups_get_done(struct tevent_req *subreq);
+static void groups_get_delete(struct tevent_req *subreq);
+
+struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ const char *name,
+ int filter_type,
+ int attrs_type)
+{
+ struct tevent_req *req, *subreq;
+ struct groups_get_state *state;
+ const char *attr_name;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct groups_get_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = ctx->be->sysdb;
+ state->domain = state->ctx->be->domain;
+ state->name = name;
+ state->filter_type = filter_type;
+
+ switch(filter_type) {
+ case BE_FILTER_NAME:
+ attr_name = ctx->opts->group_map[SDAP_AT_GROUP_NAME].name;
+ break;
+ case BE_FILTER_IDNUM:
+ attr_name = ctx->opts->group_map[SDAP_AT_GROUP_GID].name;
+ break;
+ default:
+ ret = EINVAL;
+ goto fail;
+ }
+
+ state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
+ attr_name, name,
+ ctx->opts->group_map[SDAP_OC_GROUP].name);
+ if (!state->filter) {
+ DEBUG(2, ("Failed to build filter\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ /* TODO: handle attrs_type */
+ ret = build_attrs_from_map(state, ctx->opts->group_map,
+ SDAP_OPTS_GROUP, &state->attrs);
+ if (ret != EOK) goto fail;
+
+ if (!sdap_connected(ctx)) {
+
+ if (ctx->gsh) talloc_zfree(ctx->gsh);
+
+ /* FIXME: add option to decide if tls should be used
+ * or SASL/GSSAPI, etc ... */
+ subreq = sdap_cli_connect_send(state, ev, ctx->opts,
+ ctx->be, ctx->service,
+ &ctx->rootDSE);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, groups_get_connect_done, req);
+
+ return req;
+ }
+
+ subreq = sdap_get_groups_send(state, state->ev,
+ state->domain, state->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->attrs, state->filter);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, groups_get_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void groups_get_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct groups_get_state *state = tevent_req_data(req,
+ struct groups_get_state);
+ int ret;
+
+ ret = sdap_cli_connect_recv(subreq, state->ctx,
+ &state->ctx->gsh, &state->ctx->rootDSE);
+ talloc_zfree(subreq);
+ if (ret) {
+ if (ret == ENOTSUP) {
+ DEBUG(0, ("Authentication mechanism not Supported by server"));
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_get_groups_send(state, state->ev,
+ state->domain, state->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->attrs, state->filter);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, groups_get_done, req);
+}
+
+static void groups_get_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct groups_get_state *state = tevent_req_data(req,
+ struct groups_get_state);
+ char *endptr;
+ gid_t gid;
+ int ret;
+
+ ret = sdap_get_groups_recv(subreq, NULL, NULL);
+ talloc_zfree(subreq);
+ if (ret && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (ret == ENOENT) {
+ if (strchr(state->name, '*')) {
+ /* it was an enumeration */
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ switch (state->filter_type) {
+ case BE_FILTER_NAME:
+ subreq = sysdb_delete_group_send(state, state->ev,
+ state->sysdb, NULL,
+ state->domain, state->name, 0);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, groups_get_delete, req);
+ return;
+
+ case BE_FILTER_IDNUM:
+ errno = 0;
+ gid = (gid_t)strtol(state->name, &endptr, 0);
+ if (errno || *endptr || (state->name == endptr)) {
+ tevent_req_error(req, errno);
+ return;
+ }
+
+ subreq = sysdb_delete_group_send(state, state->ev,
+ state->sysdb, NULL,
+ state->domain, NULL, gid);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, groups_get_delete, req);
+ return;
+
+ default:
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+static void groups_get_delete(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct groups_get_state *state = tevent_req_data(req,
+ struct groups_get_state);
+ int ret;
+
+ ret = sysdb_delete_group_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("Group (%s) delete returned %d (%s)\n",
+ state->name, ret, strerror(ret)));
+ }
+
+ tevent_req_done(req);
+}
+
+int groups_get_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+/* =Get-Groups-for-User================================================== */
+
+struct groups_by_user_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+ const char *name;
+ const char **attrs;
+};
+
+static void groups_by_user_connect_done(struct tevent_req *subreq);
+static void groups_by_user_done(struct tevent_req *subreq);
+
+static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ const char *name)
+{
+ struct tevent_req *req, *subreq;
+ struct groups_by_user_state *state;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct groups_by_user_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->name = name;
+
+ ret = build_attrs_from_map(state, ctx->opts->group_map,
+ SDAP_OPTS_GROUP, &state->attrs);
+ if (ret != EOK) goto fail;
+
+ if (!sdap_connected(ctx)) {
+
+ if (ctx->gsh) talloc_zfree(ctx->gsh);
+
+ /* FIXME: add option to decide if tls should be used
+ * or SASL/GSSAPI, etc ... */
+ subreq = sdap_cli_connect_send(state, ev, ctx->opts,
+ ctx->be, ctx->service,
+ &ctx->rootDSE);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, groups_by_user_connect_done, req);
+
+ return req;
+ }
+
+ subreq = sdap_get_initgr_send(state, state->ev,
+ state->ctx->be->domain,
+ state->ctx->be->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->name, state->attrs);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, groups_by_user_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void groups_by_user_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct groups_by_user_state *state = tevent_req_data(req,
+ struct groups_by_user_state);
+ int ret;
+
+ ret = sdap_cli_connect_recv(subreq, state->ctx,
+ &state->ctx->gsh, &state->ctx->rootDSE);
+ talloc_zfree(subreq);
+ if (ret) {
+ if (ret == ENOTSUP) {
+ DEBUG(0, ("Authentication mechanism not Supported by server"));
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_get_initgr_send(state, state->ev,
+ state->ctx->be->domain,
+ state->ctx->be->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->name, state->attrs);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, groups_by_user_done, req);
+}
+
+static void groups_by_user_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ int ret;
+
+ ret = sdap_get_initgr_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int groups_by_user_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+
+/* =Get-Account-Info-Call================================================= */
+
+/* FIXME: embed this function in sssd_be and only call out
+ * specific functions from modules ? */
+
+static void sdap_account_info_users_done(struct tevent_req *req);
+static void sdap_account_info_groups_done(struct tevent_req *req);
+static void sdap_account_info_initgr_done(struct tevent_req *req);
+
+void sdap_account_info_handler(struct be_req *breq)
+{
+ struct sdap_id_ctx *ctx;
+ struct be_acct_req *ar;
+ struct tevent_req *req;
+ const char *err = "Unknown Error";
+ int ret = EOK;
+
+ ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data, struct sdap_id_ctx);
+
+ if (be_is_offline(ctx->be)) {
+ return sdap_handler_done(breq, DP_ERR_OFFLINE, EAGAIN, "Offline");
+ }
+
+ ar = talloc_get_type(breq->req_data, struct be_acct_req);
+
+ switch (ar->entry_type & 0xFFF) {
+ case BE_REQ_USER: /* user */
+
+ /* skip enumerations on demand */
+ if (strcmp(ar->filter_value, "*") == 0) {
+ return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success");
+ }
+
+ req = users_get_send(breq, breq->be_ctx->ev, ctx,
+ ar->filter_value,
+ ar->filter_type,
+ ar->attr_type);
+ if (!req) {
+ return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
+ }
+
+ tevent_req_set_callback(req, sdap_account_info_users_done, breq);
+
+ break;
+
+ case BE_REQ_GROUP: /* group */
+
+ if (strcmp(ar->filter_value, "*") == 0) {
+ return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success");
+ }
+
+ /* skip enumerations on demand */
+ req = groups_get_send(breq, breq->be_ctx->ev, ctx,
+ ar->filter_value,
+ ar->filter_type,
+ ar->attr_type);
+ if (!req) {
+ return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
+ }
+
+ tevent_req_set_callback(req, sdap_account_info_groups_done, breq);
+
+ break;
+
+ case BE_REQ_INITGROUPS: /* init groups for user */
+ if (ar->filter_type != BE_FILTER_NAME) {
+ ret = EINVAL;
+ err = "Invalid filter type";
+ break;
+ }
+ if (ar->attr_type != BE_ATTR_CORE) {
+ ret = EINVAL;
+ err = "Invalid attr type";
+ break;
+ }
+ if (strchr(ar->filter_value, '*')) {
+ ret = EINVAL;
+ err = "Invalid filter value";
+ break;
+ }
+ req = groups_by_user_send(breq, breq->be_ctx->ev, ctx,
+ ar->filter_value);
+ if (!req) ret = ENOMEM;
+ /* tevent_req_set_callback(req, groups_by_user_done, breq); */
+
+ tevent_req_set_callback(req, sdap_account_info_initgr_done, breq);
+
+ break;
+
+ default: /*fail*/
+ ret = EINVAL;
+ err = "Invalid request type";
+ }
+
+ if (ret != EOK) return sdap_handler_done(breq, DP_ERR_FATAL, ret, err);
+}
+
+static void sdap_account_info_users_done(struct tevent_req *req)
+{
+ struct be_req *breq = tevent_req_callback_data(req, struct be_req);
+ struct sdap_id_ctx *ctx;
+ int dp_err = DP_ERR_OK;
+ const char *error = NULL;
+ int ret;
+
+ ret = users_get_recv(req);
+ talloc_zfree(req);
+
+ if (ret) {
+ dp_err = DP_ERR_FATAL;
+ error = "Enum Users Failed";
+
+ if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) {
+ dp_err = DP_ERR_OFFLINE;
+ ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data,
+ struct sdap_id_ctx);
+ if (sdap_check_gssapi_reconnect(ctx)) {
+ talloc_zfree(ctx->gsh);
+ sdap_account_info_handler(breq);
+ return;
+ }
+ sdap_mark_offline(ctx);
+ }
+ }
+
+ sdap_handler_done(breq, dp_err, ret, error);
+}
+
+static void sdap_account_info_groups_done(struct tevent_req *req)
+{
+ struct be_req *breq = tevent_req_callback_data(req, struct be_req);
+ struct sdap_id_ctx *ctx;
+ int dp_err = DP_ERR_OK;
+ const char *error = NULL;
+ int ret;
+
+ ret = groups_get_recv(req);
+ talloc_zfree(req);
+
+ if (ret) {
+ dp_err = DP_ERR_FATAL;
+ error = "Enum Groups Failed";
+
+ if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) {
+ dp_err = DP_ERR_OFFLINE;
+ ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data,
+ struct sdap_id_ctx);
+ if (sdap_check_gssapi_reconnect(ctx)) {
+ talloc_zfree(ctx->gsh);
+ sdap_account_info_handler(breq);
+ return;
+ }
+ sdap_mark_offline(ctx);
+ }
+ }
+
+ return sdap_handler_done(breq, dp_err, ret, error);
+}
+
+static void sdap_account_info_initgr_done(struct tevent_req *req)
+{
+ struct be_req *breq = tevent_req_callback_data(req, struct be_req);
+ struct sdap_id_ctx *ctx;
+ int dp_err = DP_ERR_OK;
+ const char *error = NULL;
+ int ret;
+
+ ret = groups_by_user_recv(req);
+ talloc_zfree(req);
+
+ if (ret) {
+ dp_err = DP_ERR_FATAL;
+ error = "Init Groups Failed";
+
+ if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) {
+ dp_err = DP_ERR_OFFLINE;
+ ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data,
+ struct sdap_id_ctx);
+ if (sdap_check_gssapi_reconnect(ctx)) {
+ talloc_zfree(ctx->gsh);
+ sdap_account_info_handler(breq);
+ return;
+ }
+ sdap_mark_offline(ctx);
+ }
+ }
+
+ return sdap_handler_done(breq, dp_err, ret, error);
+}
+
diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c
new file mode 100644
index 000000000..f3fb4443c
--- /dev/null
+++ b/src/providers/ldap/ldap_id_cleanup.c
@@ -0,0 +1,555 @@
+/*
+ SSSD
+
+ LDAP Identity Cleanup Functions
+
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ 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 <time.h>
+#include <sys/time.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async.h"
+
+/* ==Cleanup-Task========================================================= */
+
+struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx);
+static void ldap_id_cleanup_reschedule(struct tevent_req *req);
+
+static void ldap_id_cleanup_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt);
+
+static void ldap_id_cleanup_timer(struct tevent_context *ev,
+ struct tevent_timer *tt,
+ struct timeval tv, void *pvt)
+{
+ struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx);
+ struct tevent_timer *timeout;
+ struct tevent_req *req;
+ int delay;
+
+ if (be_is_offline(ctx->be)) {
+ DEBUG(4, ("Backend is marked offline, retry later!\n"));
+ /* schedule starting from now, not the last run */
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+ tv = tevent_timeval_current_ofs(delay, 0);
+ ldap_id_cleanup_set_timer(ctx, tv);
+ return;
+ }
+
+ req = ldap_id_cleanup_send(ctx, ev, ctx);
+ if (!req) {
+ DEBUG(1, ("Failed to schedule cleanup, retrying later!\n"));
+ /* schedule starting from now, not the last run */
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+ tv = tevent_timeval_current_ofs(delay, 0);
+ ldap_id_cleanup_set_timer(ctx, tv);
+ return;
+ }
+ tevent_req_set_callback(req, ldap_id_cleanup_reschedule, ctx);
+
+ /* if cleanup takes so long, either we try to cleanup too
+ * frequently, or something went seriously wrong */
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+ tv = tevent_timeval_current_ofs(delay, 0);
+ timeout = tevent_add_timer(ctx->be->ev, req, tv,
+ ldap_id_cleanup_timeout, req);
+ return;
+}
+
+static void ldap_id_cleanup_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
+ struct sdap_id_ctx);
+ int delay;
+
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+ DEBUG(1, ("Cleanup timed out! Timeout too small? (%ds)!\n", delay));
+
+ tv = tevent_timeval_current_ofs(delay, 0);
+ ldap_id_enumerate_set_timer(ctx, tv);
+
+ talloc_zfree(req);
+}
+
+static void ldap_id_cleanup_reschedule(struct tevent_req *req)
+{
+ struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
+ struct sdap_id_ctx);
+ enum tevent_req_state tstate;
+ uint64_t err;
+ struct timeval tv;
+ int delay;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ /* On error schedule starting from now, not the last run */
+ tv = tevent_timeval_current();
+ } else {
+ tv = ctx->last_purge;
+ }
+ talloc_zfree(req);
+
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+ tv = tevent_timeval_add(&tv, delay, 0);
+ ldap_id_enumerate_set_timer(ctx, tv);
+}
+
+
+
+int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
+{
+ struct tevent_timer *cleanup_task;
+
+ DEBUG(6, ("Scheduling next cleanup at %ld.%ld\n",
+ (long)tv.tv_sec, (long)tv.tv_usec));
+
+ cleanup_task = tevent_add_timer(ctx->be->ev, ctx,
+ tv, ldap_id_cleanup_timer, ctx);
+ if (!cleanup_task) {
+ DEBUG(0, ("FATAL: failed to setup cleanup task!\n"));
+ return EFAULT;
+ }
+
+ return EOK;
+}
+
+
+
+struct global_cleanup_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+};
+
+static struct tevent_req *cleanup_users_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx);
+static void ldap_id_cleanup_users_done(struct tevent_req *subreq);
+static struct tevent_req *cleanup_groups_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx);
+static void ldap_id_cleanup_groups_done(struct tevent_req *subreq);
+
+struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx)
+{
+ struct global_cleanup_state *state;
+ struct tevent_req *req, *subreq;
+
+ req = tevent_req_create(memctx, &state, struct global_cleanup_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+
+ subreq = cleanup_users_send(state, ev, ctx);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, ldap_id_cleanup_users_done, req);
+
+ ctx->last_purge = tevent_timeval_current();
+
+ return req;
+}
+
+static void ldap_id_cleanup_users_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct global_cleanup_state *state = tevent_req_data(req,
+ struct global_cleanup_state);
+ enum tevent_req_state tstate;
+ uint64_t err = 0;
+
+ if (tevent_req_is_error(subreq, &tstate, &err)) {
+ if (tstate != TEVENT_REQ_USER_ERROR) {
+ err = EIO;
+ }
+ if (err != ENOENT) {
+ goto fail;
+ }
+ }
+ talloc_zfree(subreq);
+
+ subreq = cleanup_groups_send(state, state->ev, state->ctx);
+ if (!subreq) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, ldap_id_cleanup_groups_done, req);
+
+ return;
+
+fail:
+ if (err) {
+ DEBUG(9, ("User cleanup failed with: (%d)[%s]\n",
+ (int)err, strerror(err)));
+
+ if (sdap_check_gssapi_reconnect(state->ctx)) {
+ talloc_zfree(state->ctx->gsh);
+ subreq = cleanup_users_send(state, state->ev, state->ctx);
+ if (subreq != NULL) {
+ tevent_req_set_callback(subreq, ldap_id_cleanup_users_done, req);
+ return;
+ }
+ }
+ sdap_mark_offline(state->ctx);
+ }
+
+ DEBUG(1, ("Failed to cleanup users, retrying later!\n"));
+ tevent_req_done(req);
+}
+
+static void ldap_id_cleanup_groups_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct global_cleanup_state *state = tevent_req_data(req,
+ struct global_cleanup_state);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(subreq, &tstate, &err)) {
+ if (tstate != TEVENT_REQ_USER_ERROR) {
+ err = EIO;
+ }
+ if (err != ENOENT) {
+ goto fail;
+ }
+ }
+ talloc_zfree(subreq);
+
+ tevent_req_done(req);
+ return;
+
+fail:
+ /* check if credentials are expired otherwise go offline on failures */
+ if (sdap_check_gssapi_reconnect(state->ctx)) {
+ talloc_zfree(state->ctx->gsh);
+ subreq = cleanup_groups_send(state, state->ev, state->ctx);
+ if (subreq != NULL) {
+ tevent_req_set_callback(subreq, ldap_id_cleanup_groups_done, req);
+ return;
+ }
+ }
+ sdap_mark_offline(state->ctx);
+ DEBUG(1, ("Failed to cleanup groups (%d [%s]), retrying later!\n",
+ (int)err, strerror(err)));
+ tevent_req_done(req);
+}
+
+
+/* ==User-Cleanup-Process================================================= */
+
+struct cleanup_users_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+
+ struct sysdb_handle *handle;
+
+ struct ldb_message **msgs;
+ size_t count;
+ int cur;
+};
+
+static void cleanup_users_process(struct tevent_req *subreq);
+static void cleanup_users_update(struct tevent_req *req);
+static void cleanup_users_up_done(struct tevent_req *subreq);
+
+static struct tevent_req *cleanup_users_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx)
+{
+ struct tevent_req *req, *subreq;
+ struct cleanup_users_state *state;
+ static const char *attrs[] = { SYSDB_NAME, NULL };
+ time_t now = time(NULL);
+ char *subfilter;
+
+ req = tevent_req_create(memctx, &state, struct cleanup_users_state);
+ if (!req) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = ctx->be->sysdb;
+ state->domain = ctx->be->domain;
+ state->msgs = NULL;
+ state->count = 0;
+ state->cur = 0;
+
+ subfilter = talloc_asprintf(state, "(&(!(%s=0))(%s<=%ld))",
+ SYSDB_CACHE_EXPIRE,
+ SYSDB_CACHE_EXPIRE, (long)now);
+ if (!subfilter) {
+ DEBUG(2, ("Failed to build filter\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ subreq = sysdb_search_users_send(state, state->ev,
+ state->sysdb, NULL,
+ state->domain, subfilter, attrs);
+ if (!subreq) {
+ DEBUG(2, ("Failed to send entry search\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, cleanup_users_process, req);
+
+ return req;
+}
+
+static void cleanup_users_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cleanup_users_state *state = tevent_req_data(req,
+ struct cleanup_users_state);
+ int ret;
+
+ ret = sysdb_search_users_recv(subreq, state, &state->count, &state->msgs);
+ talloc_zfree(subreq);
+ if (ret) {
+ if (ret == ENOENT) {
+ tevent_req_done(req);
+ return;
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(4, ("Found %d expired user entries!\n", state->count));
+
+ if (state->count == 0) {
+ tevent_req_done(req);
+ }
+
+ cleanup_users_update(req);
+}
+
+static void cleanup_users_update(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct cleanup_users_state *state = tevent_req_data(req,
+ struct cleanup_users_state);
+ const char *str;
+
+ str = ldb_msg_find_attr_as_string(state->msgs[state->cur],
+ SYSDB_NAME, NULL);
+ if (!str) {
+ DEBUG(2, ("Entry %s has no Name Attribute ?!?\n",
+ ldb_dn_get_linearized(state->msgs[state->cur]->dn)));
+ tevent_req_error(req, EFAULT);
+ return;
+ }
+
+ subreq = users_get_send(state, state->ev, state->ctx,
+ str, BE_FILTER_NAME, BE_ATTR_CORE);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, cleanup_users_up_done, req);
+}
+
+static void cleanup_users_up_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cleanup_users_state *state = tevent_req_data(req,
+ struct cleanup_users_state);
+ int ret;
+
+ ret = users_get_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("User check returned: %d(%s)\n",
+ ret, strerror(ret)));
+ }
+
+ /* if the entry doesn't need to be purged, remove it from the list */
+ if (ret != ENOENT) {
+ talloc_zfree(state->msgs[state->cur]);
+ }
+
+ state->cur++;
+ if (state->cur < state->count) {
+ cleanup_users_update(req);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+/* ==Group-Cleanup-Process================================================ */
+
+struct cleanup_groups_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+
+ struct sysdb_handle *handle;
+
+ struct ldb_message **msgs;
+ size_t count;
+ int cur;
+};
+
+static void cleanup_groups_process(struct tevent_req *subreq);
+static void cleanup_groups_update(struct tevent_req *req);
+static void cleanup_groups_up_done(struct tevent_req *subreq);
+
+static struct tevent_req *cleanup_groups_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx)
+{
+ struct tevent_req *req, *subreq;
+ struct cleanup_groups_state *state;
+ static const char *attrs[] = { SYSDB_NAME, NULL };
+ time_t now = time(NULL);
+ char *subfilter;
+
+ req = tevent_req_create(memctx, &state, struct cleanup_groups_state);
+ if (!req) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->ctx = ctx;
+ state->sysdb = ctx->be->sysdb;
+ state->domain = ctx->be->domain;
+ state->msgs = NULL;
+ state->count = 0;
+ state->cur = 0;
+
+ subfilter = talloc_asprintf(state, "(&(!(%s=0))(%s<=%ld))",
+ SYSDB_CACHE_EXPIRE,
+ SYSDB_CACHE_EXPIRE, (long)now);
+ if (!subfilter) {
+ DEBUG(2, ("Failed to build filter\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ subreq = sysdb_search_groups_send(state, state->ev,
+ state->sysdb, NULL,
+ state->domain, subfilter, attrs);
+ if (!subreq) {
+ DEBUG(2, ("Failed to send entry search\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, cleanup_groups_process, req);
+
+ return req;
+}
+
+static void cleanup_groups_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cleanup_groups_state *state = tevent_req_data(req,
+ struct cleanup_groups_state);
+ int ret;
+
+ ret = sysdb_search_groups_recv(subreq, state, &state->count, &state->msgs);
+ talloc_zfree(subreq);
+ if (ret) {
+ if (ret == ENOENT) {
+ tevent_req_done(req);
+ return;
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(4, ("Found %d expired group entries!\n", state->count));
+
+ if (state->count == 0) {
+ tevent_req_done(req);
+ }
+
+ cleanup_groups_update(req);
+}
+
+static void cleanup_groups_update(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct cleanup_groups_state *state = tevent_req_data(req,
+ struct cleanup_groups_state);
+ const char *str;
+
+ str = ldb_msg_find_attr_as_string(state->msgs[state->cur],
+ SYSDB_NAME, NULL);
+ if (!str) {
+ DEBUG(2, ("Entry %s has no Name Attribute ?!?\n",
+ ldb_dn_get_linearized(state->msgs[state->cur]->dn)));
+ tevent_req_error(req, EFAULT);
+ return;
+ }
+
+ subreq = groups_get_send(state, state->ev, state->ctx,
+ str, BE_FILTER_NAME, BE_ATTR_CORE);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, cleanup_groups_up_done, req);
+}
+
+static void cleanup_groups_up_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cleanup_groups_state *state = tevent_req_data(req,
+ struct cleanup_groups_state);
+ int ret;
+
+ ret = groups_get_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("User check returned: %d(%s)\n",
+ ret, strerror(ret)));
+ }
+
+ state->cur++;
+ if (state->cur < state->count) {
+ cleanup_groups_update(req);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
new file mode 100644
index 000000000..bc06e8bdc
--- /dev/null
+++ b/src/providers/ldap/ldap_id_enum.c
@@ -0,0 +1,608 @@
+/*
+ SSSD
+
+ LDAP Identity Enumeration
+
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ 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 <time.h>
+#include <sys/time.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async.h"
+
+extern struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx);
+
+/* ==Enumeration-Task===================================================== */
+
+static struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev,
+ struct sdap_id_ctx *ctx);
+
+static void ldap_id_enumerate_reschedule(struct tevent_req *req);
+
+static void ldap_id_enumerate_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt);
+
+static void ldap_id_enumerate_timer(struct tevent_context *ev,
+ struct tevent_timer *tt,
+ struct timeval tv, void *pvt)
+{
+ struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx);
+ struct tevent_timer *timeout;
+ struct tevent_req *req;
+ int delay;
+
+ if (be_is_offline(ctx->be)) {
+ DEBUG(4, ("Backend is marked offline, retry later!\n"));
+ /* schedule starting from now, not the last run */
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
+ tv = tevent_timeval_current_ofs(delay, 0);
+ ldap_id_enumerate_set_timer(ctx, tv);
+ return;
+ }
+
+ req = ldap_id_enumerate_send(ev, ctx);
+ if (!req) {
+ DEBUG(1, ("Failed to schedule enumeration, retrying later!\n"));
+ /* schedule starting from now, not the last run */
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
+ tv = tevent_timeval_current_ofs(delay, 0);
+ ldap_id_enumerate_set_timer(ctx, tv);
+ return;
+ }
+ tevent_req_set_callback(req, ldap_id_enumerate_reschedule, ctx);
+
+ /* if enumeration takes so long, either we try to enumerate too
+ * frequently, or something went seriously wrong */
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
+ tv = tevent_timeval_current_ofs(delay, 0);
+ timeout = tevent_add_timer(ctx->be->ev, req, tv,
+ ldap_id_enumerate_timeout, req);
+ return;
+}
+
+static void ldap_id_enumerate_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
+ struct sdap_id_ctx);
+ int delay;
+
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
+ DEBUG(1, ("Enumeration timed out! Timeout too small? (%ds)!\n", delay));
+
+ tv = tevent_timeval_current_ofs(delay, 0);
+ ldap_id_enumerate_set_timer(ctx, tv);
+
+ talloc_zfree(req);
+}
+
+static void ldap_id_enumerate_reschedule(struct tevent_req *req)
+{
+ struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
+ struct sdap_id_ctx);
+ enum tevent_req_state tstate;
+ uint64_t err;
+ struct timeval tv;
+ int delay;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ /* On error schedule starting from now, not the last run */
+ tv = tevent_timeval_current();
+ } else {
+ tv = ctx->last_enum;
+ }
+ talloc_zfree(req);
+
+ delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
+ tv = tevent_timeval_add(&tv, delay, 0);
+ ldap_id_enumerate_set_timer(ctx, tv);
+}
+
+
+
+int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
+{
+ struct tevent_timer *enum_task;
+
+ DEBUG(6, ("Scheduling next enumeration at %ld.%ld\n",
+ (long)tv.tv_sec, (long)tv.tv_usec));
+
+ enum_task = tevent_add_timer(ctx->be->ev, ctx,
+ tv, ldap_id_enumerate_timer, ctx);
+ if (!enum_task) {
+ DEBUG(0, ("FATAL: failed to setup enumeration task!\n"));
+ return EFAULT;
+ }
+
+ return EOK;
+}
+
+
+struct global_enum_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+
+ bool purge;
+};
+
+static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ bool purge);
+static void ldap_id_enum_users_done(struct tevent_req *subreq);
+static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ bool purge);
+static void ldap_id_enum_groups_done(struct tevent_req *subreq);
+static void ldap_id_enum_cleanup_done(struct tevent_req *subreq);
+
+static struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev,
+ struct sdap_id_ctx *ctx)
+{
+ struct global_enum_state *state;
+ struct tevent_req *req, *subreq;
+ int t;
+
+ req = tevent_req_create(ctx, &state, struct global_enum_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+
+ ctx->last_enum = tevent_timeval_current();
+
+ t = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+ if ((ctx->last_purge.tv_sec + t) < ctx->last_enum.tv_sec) {
+ state->purge = true;
+ } else {
+ state->purge = false;
+ }
+
+ subreq = enum_users_send(state, ev, ctx, state->purge);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, ldap_id_enum_users_done, req);
+
+ return req;
+}
+
+static void ldap_id_enum_users_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct global_enum_state *state = tevent_req_data(req,
+ struct global_enum_state);
+ enum tevent_req_state tstate;
+ uint64_t err = 0;
+
+ if (tevent_req_is_error(subreq, &tstate, &err)) {
+ if (tstate != TEVENT_REQ_USER_ERROR) {
+ err = EIO;
+ }
+ if (err != ENOENT) {
+ goto fail;
+ }
+ }
+ talloc_zfree(subreq);
+
+ subreq = enum_groups_send(state, state->ev, state->ctx, state->purge);
+ if (!subreq) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req);
+
+ return;
+
+fail:
+ if (err) {
+ DEBUG(9, ("User enumeration failed with: (%d)[%s]\n",
+ (int)err, strerror(err)));
+
+ if (sdap_check_gssapi_reconnect(state->ctx)) {
+ talloc_zfree(state->ctx->gsh);
+ subreq = enum_users_send(state, state->ev, state->ctx, state->purge);
+ if (subreq != NULL) {
+ tevent_req_set_callback(subreq, ldap_id_enum_users_done, req);
+ return;
+ }
+ }
+ sdap_mark_offline(state->ctx);
+ }
+
+ DEBUG(1, ("Failed to enumerate users, retrying later!\n"));
+ tevent_req_done(req);
+}
+
+static void ldap_id_enum_groups_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct global_enum_state *state = tevent_req_data(req,
+ struct global_enum_state);
+ enum tevent_req_state tstate;
+ uint64_t err = 0;
+
+ if (tevent_req_is_error(subreq, &tstate, &err)) {
+ if (tstate != TEVENT_REQ_USER_ERROR) {
+ err = EIO;
+ }
+ if (err != ENOENT) {
+ goto fail;
+ }
+ }
+ talloc_zfree(subreq);
+
+ if (state->purge) {
+
+ subreq = ldap_id_cleanup_send(state, state->ev, state->ctx);
+ if (!subreq) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, ldap_id_enum_cleanup_done, req);
+
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+
+fail:
+ /* check if credentials are expired otherwise go offline on failures */
+ if (sdap_check_gssapi_reconnect(state->ctx)) {
+ talloc_zfree(state->ctx->gsh);
+ subreq = enum_groups_send(state, state->ev, state->ctx, state->purge);
+ if (subreq != NULL) {
+ tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req);
+ return;
+ }
+ }
+ sdap_mark_offline(state->ctx);
+ DEBUG(1, ("Failed to enumerate groups (%d [%s]), retrying later!\n",
+ (int)err, strerror(err)));
+ tevent_req_done(req);
+}
+
+static void ldap_id_enum_cleanup_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ talloc_zfree(subreq);
+ tevent_req_done(req);
+}
+
+
+/* ==User-Enumeration===================================================== */
+
+struct enum_users_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+
+ char *filter;
+ const char **attrs;
+};
+
+static void enum_users_connect_done(struct tevent_req *subreq);
+static void enum_users_op_done(struct tevent_req *subreq);
+
+static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ bool purge)
+{
+ struct tevent_req *req, *subreq;
+ struct enum_users_state *state;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct enum_users_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+
+ if (ctx->max_user_timestamp && !purge) {
+
+ state->filter = talloc_asprintf(state,
+ "(&(%s=*)(objectclass=%s)(%s>=%s)(!(%s=%s)))",
+ ctx->opts->user_map[SDAP_AT_USER_NAME].name,
+ ctx->opts->user_map[SDAP_OC_USER].name,
+ ctx->opts->user_map[SDAP_AT_USER_MODSTAMP].name,
+ ctx->max_user_timestamp,
+ ctx->opts->user_map[SDAP_AT_USER_MODSTAMP].name,
+ ctx->max_user_timestamp);
+ } else {
+ state->filter = talloc_asprintf(state,
+ "(&(%s=*)(objectclass=%s))",
+ ctx->opts->user_map[SDAP_AT_USER_NAME].name,
+ ctx->opts->user_map[SDAP_OC_USER].name);
+ }
+ if (!state->filter) {
+ DEBUG(2, ("Failed to build filter\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ /* TODO: handle attrs_type */
+ ret = build_attrs_from_map(state, ctx->opts->user_map,
+ SDAP_OPTS_USER, &state->attrs);
+ if (ret != EOK) goto fail;
+
+ if (!sdap_connected(ctx)) {
+
+ if (ctx->gsh) talloc_zfree(ctx->gsh);
+
+ /* FIXME: add option to decide if tls should be used
+ * or SASL/GSSAPI, etc ... */
+ subreq = sdap_cli_connect_send(state, ev, ctx->opts,
+ ctx->be, ctx->service,
+ &ctx->rootDSE);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, enum_users_connect_done, req);
+
+ return req;
+ }
+
+ subreq = sdap_get_users_send(state, state->ev,
+ state->ctx->be->domain,
+ state->ctx->be->sysdb,
+ state->ctx->opts,
+ state->ctx->gsh,
+ state->attrs, state->filter);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, enum_users_op_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void enum_users_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct enum_users_state *state = tevent_req_data(req,
+ struct enum_users_state);
+ int ret;
+
+ ret = sdap_cli_connect_recv(subreq, state->ctx,
+ &state->ctx->gsh, &state->ctx->rootDSE);
+ talloc_zfree(subreq);
+ if (ret) {
+ if (ret == ENOTSUP) {
+ DEBUG(0, ("Authentication mechanism not Supported by server"));
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_get_users_send(state, state->ev,
+ state->ctx->be->domain,
+ state->ctx->be->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->attrs, state->filter);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, enum_users_op_done, req);
+}
+
+static void enum_users_op_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct enum_users_state *state = tevent_req_data(req,
+ struct enum_users_state);
+ char *timestamp;
+ int ret;
+
+ ret = sdap_get_users_recv(subreq, state, &timestamp);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (timestamp) {
+ talloc_zfree(state->ctx->max_user_timestamp);
+ state->ctx->max_user_timestamp = talloc_steal(state->ctx, timestamp);
+ }
+
+ DEBUG(4, ("Users higher timestamp: [%s]\n",
+ state->ctx->max_user_timestamp));
+
+ tevent_req_done(req);
+}
+
+/* =Group-Enumeration===================================================== */
+
+struct enum_groups_state {
+ struct tevent_context *ev;
+ struct sdap_id_ctx *ctx;
+
+ char *filter;
+ const char **attrs;
+};
+
+static void enum_groups_connect_done(struct tevent_req *subreq);
+static void enum_groups_op_done(struct tevent_req *subreq);
+
+static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_id_ctx *ctx,
+ bool purge)
+{
+ struct tevent_req *req, *subreq;
+ struct enum_groups_state *state;
+ const char *attr_name;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct enum_groups_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+
+ attr_name = ctx->opts->group_map[SDAP_AT_GROUP_NAME].name;
+
+ if (ctx->max_group_timestamp && !purge) {
+
+ state->filter = talloc_asprintf(state,
+ "(&(%s=*)(objectclass=%s)(%s>=%s)(!(%s=%s)))",
+ ctx->opts->group_map[SDAP_AT_GROUP_NAME].name,
+ ctx->opts->group_map[SDAP_OC_GROUP].name,
+ ctx->opts->group_map[SDAP_AT_GROUP_MODSTAMP].name,
+ ctx->max_group_timestamp,
+ ctx->opts->group_map[SDAP_AT_GROUP_MODSTAMP].name,
+ ctx->max_group_timestamp);
+ } else {
+ state->filter = talloc_asprintf(state,
+ "(&(%s=*)(objectclass=%s))",
+ ctx->opts->group_map[SDAP_AT_GROUP_NAME].name,
+ ctx->opts->group_map[SDAP_OC_GROUP].name);
+ }
+ if (!state->filter) {
+ DEBUG(2, ("Failed to build filter\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ /* TODO: handle attrs_type */
+ ret = build_attrs_from_map(state, ctx->opts->group_map,
+ SDAP_OPTS_GROUP, &state->attrs);
+ if (ret != EOK) goto fail;
+
+ if (!sdap_connected(ctx)) {
+
+ if (ctx->gsh) talloc_zfree(ctx->gsh);
+
+ /* FIXME: add option to decide if tls should be used
+ * or SASL/GSSAPI, etc ... */
+ subreq = sdap_cli_connect_send(state, ev, ctx->opts,
+ ctx->be, ctx->service,
+ &ctx->rootDSE);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, enum_groups_connect_done, req);
+
+ return req;
+ }
+
+ subreq = sdap_get_groups_send(state, state->ev,
+ state->ctx->be->domain,
+ state->ctx->be->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->attrs, state->filter);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, enum_groups_op_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void enum_groups_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct enum_groups_state *state = tevent_req_data(req,
+ struct enum_groups_state);
+ int ret;
+
+ ret = sdap_cli_connect_recv(subreq, state->ctx,
+ &state->ctx->gsh, &state->ctx->rootDSE);
+ talloc_zfree(subreq);
+ if (ret) {
+ if (ret == ENOTSUP) {
+ DEBUG(0, ("Authentication mechanism not Supported by server"));
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_get_groups_send(state, state->ev,
+ state->ctx->be->domain,
+ state->ctx->be->sysdb,
+ state->ctx->opts, state->ctx->gsh,
+ state->attrs, state->filter);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, enum_groups_op_done, req);
+}
+
+static void enum_groups_op_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct enum_groups_state *state = tevent_req_data(req,
+ struct enum_groups_state);
+ char *timestamp;
+ int ret;
+
+ ret = sdap_get_groups_recv(subreq, state, &timestamp);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (timestamp) {
+ talloc_zfree(state->ctx->max_group_timestamp);
+ state->ctx->max_group_timestamp = talloc_steal(state->ctx, timestamp);
+ }
+
+ DEBUG(4, ("Groups higher timestamp: [%s]\n",
+ state->ctx->max_group_timestamp));
+
+ tevent_req_done(req);
+}
+
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
new file mode 100644
index 000000000..b1f053fbc
--- /dev/null
+++ b/src/providers/ldap/ldap_init.c
@@ -0,0 +1,179 @@
+/*
+ SSSD
+
+ LDAP Provider Initialization functions
+
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ 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 "providers/child_common.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async_private.h"
+
+static void sdap_shutdown(struct be_req *req);
+
+/* Id Handler */
+struct bet_ops sdap_id_ops = {
+ .handler = sdap_account_info_handler,
+ .finalize = sdap_shutdown
+};
+
+/* Auth Handler */
+struct bet_ops sdap_auth_ops = {
+ .handler = sdap_pam_auth_handler,
+ .finalize = sdap_shutdown
+};
+
+/* Chpass Handler */
+struct bet_ops sdap_chpass_ops = {
+ .handler = sdap_pam_chpass_handler,
+ .finalize = sdap_shutdown
+};
+
+int sssm_ldap_init(struct be_ctx *bectx,
+ struct bet_ops **ops,
+ void **pvt_data)
+{
+ struct sdap_id_ctx *ctx;
+ const char *urls;
+ int ret;
+
+ ctx = talloc_zero(bectx, struct sdap_id_ctx);
+ if (!ctx) return ENOMEM;
+
+ ctx->be = bectx;
+
+ ret = ldap_get_options(ctx, bectx->cdb,
+ bectx->conf_path, &ctx->opts);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ urls = dp_opt_get_string(ctx->opts->basic, SDAP_URI);
+ if (!urls) {
+ DEBUG(0, ("Missing ldap_uri\n"));
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sdap_service_init(ctx, ctx->be, "LDAP", urls, &ctx->service);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to initialize failover service!\n"));
+ goto done;
+ }
+
+ ret = setup_tls_config(ctx->opts->basic);
+ if (ret != EOK) {
+ DEBUG(1, ("setup_tls_config failed [%d][%s].\n",
+ ret, strerror(ret)));
+ goto done;
+ }
+
+ ret = sdap_id_setup_tasks(ctx);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = setup_child(ctx);
+ if (ret != EOK) {
+ DEBUG(1, ("setup_child failed [%d][%s].\n",
+ ret, strerror(ret)));
+ goto done;
+ }
+
+ *ops = &sdap_id_ops;
+ *pvt_data = ctx;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(ctx);
+ }
+ return ret;
+}
+
+int sssm_ldap_auth_init(struct be_ctx *bectx,
+ struct bet_ops **ops,
+ void **pvt_data)
+{
+ struct sdap_auth_ctx *ctx;
+ const char *urls;
+ int ret;
+
+ ctx = talloc(bectx, struct sdap_auth_ctx);
+ if (!ctx) return ENOMEM;
+
+ ctx->be = bectx;
+
+ ret = ldap_get_options(ctx, bectx->cdb,
+ bectx->conf_path, &ctx->opts);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ urls = dp_opt_get_string(ctx->opts->basic, SDAP_URI);
+ if (!urls) {
+ DEBUG(0, ("Missing ldap_uri\n"));
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sdap_service_init(ctx, ctx->be, "LDAP", urls, &ctx->service);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to initialize failover service!\n"));
+ goto done;
+ }
+
+ ret = setup_tls_config(ctx->opts->basic);
+ if (ret != EOK) {
+ DEBUG(1, ("setup_tls_config failed [%d][%s].\n",
+ ret, strerror(ret)));
+ goto done;
+ }
+
+ *ops = &sdap_auth_ops;
+ *pvt_data = ctx;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(ctx);
+ }
+ return ret;
+}
+
+int sssm_ldap_chpass_init(struct be_ctx *bectx,
+ struct bet_ops **ops,
+ void **pvt_data)
+{
+ int ret;
+
+ ret = sssm_ldap_auth_init(bectx, ops, pvt_data);
+
+ *ops = &sdap_chpass_ops;
+
+ return ret;
+}
+
+static void sdap_shutdown(struct be_req *req)
+{
+ /* TODO: Clean up any internal data */
+ sdap_handler_done(req, DP_ERR_OK, EOK, NULL);
+}
+
diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
new file mode 100644
index 000000000..39c67cc92
--- /dev/null
+++ b/src/providers/ldap/sdap.c
@@ -0,0 +1,388 @@
+/*
+ SSSD
+
+ LDAP Helper routines
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com>
+
+ 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/>.
+*/
+
+#define LDAP_DEPRECATED 1
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "providers/ldap/sdap.h"
+
+/* =Retrieve-Options====================================================== */
+
+int sdap_get_map(TALLOC_CTX *memctx,
+ struct confdb_ctx *cdb,
+ const char *conf_path,
+ struct sdap_attr_map *def_map,
+ int num_entries,
+ struct sdap_attr_map **_map)
+{
+ struct sdap_attr_map *map;
+ int i, ret;
+
+ map = talloc_array(memctx, struct sdap_attr_map, num_entries);
+ if (!map) {
+ return ENOMEM;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+
+ map[i].opt_name = def_map[i].opt_name;
+ map[i].def_name = def_map[i].def_name;
+ map[i].sys_name = def_map[i].sys_name;
+
+ ret = confdb_get_string(cdb, map, conf_path,
+ map[i].opt_name,
+ map[i].def_name,
+ &map[i].name);
+ if ((ret != EOK) || (map[i].def_name && !map[i].name)) {
+ DEBUG(0, ("Failed to retrieve value for %s\n", map[i].opt_name));
+ if (ret != EOK) {
+ talloc_zfree(map);
+ return EINVAL;
+ }
+ }
+
+ DEBUG(5, ("Option %s has value %s\n", map[i].opt_name, map[i].name));
+ }
+
+ *_map = map;
+ return EOK;
+}
+
+/* =Parse-msg============================================================= */
+
+int sdap_parse_entry(TALLOC_CTX *memctx,
+ struct sdap_handle *sh, struct sdap_msg *sm,
+ struct sdap_attr_map *map, int attrs_num,
+ struct sysdb_attrs **_attrs, char **_dn)
+{
+ struct sysdb_attrs *attrs;
+ BerElement *ber = NULL;
+ struct berval **vals;
+ struct ldb_val v;
+ char *str;
+ int lerrno;
+ int a, i, ret;
+ const char *name;
+ bool store;
+
+ lerrno = 0;
+ ldap_set_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+
+ attrs = sysdb_new_attrs(memctx);
+ if (!attrs) return ENOMEM;
+
+ str = ldap_get_dn(sh->ldap, sm->msg);
+ if (!str) {
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ DEBUG(1, ("ldap_get_dn failed: %d(%s)\n",
+ lerrno, ldap_err2string(lerrno)));
+ ret = EIO;
+ goto fail;
+ }
+
+ DEBUG(9, ("OriginalDN: [%s].\n", str));
+ ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, str);
+ if (ret) goto fail;
+ if (_dn) {
+ *_dn = talloc_strdup(memctx, str);
+ if (!*_dn) {
+ ret = ENOMEM;
+ ldap_memfree(str);
+ goto fail;
+ }
+ }
+ ldap_memfree(str);
+
+ if (map) {
+ vals = ldap_get_values_len(sh->ldap, sm->msg, "objectClass");
+ if (!vals) {
+ DEBUG(1, ("Unknown entry type, no objectClasses found!\n"));
+ ret = EINVAL;
+ goto fail;
+ }
+
+ for (i = 0; vals[i]; i++) {
+ /* the objectclass is always the first name in the map */
+ if (strncasecmp(map[0].name,
+ vals[i]->bv_val, vals[i]->bv_len) == 0) {
+ /* ok it's an entry of the right type */
+ break;
+ }
+ }
+ if (!vals[i]) {
+ DEBUG(1, ("objectClass not matching: %s\n",
+ map[0].name));
+ ldap_value_free_len(vals);
+ ret = EINVAL;
+ goto fail;
+ }
+ ldap_value_free_len(vals);
+ }
+
+ str = ldap_first_attribute(sh->ldap, sm->msg, &ber);
+ if (!str) {
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ DEBUG(1, ("Entry has no attributes [%d(%s)]!?\n",
+ lerrno, ldap_err2string(lerrno)));
+ if (map) {
+ ret = EINVAL;
+ goto fail;
+ }
+ }
+ while (str) {
+ if (map) {
+ for (a = 1; a < attrs_num; a++) {
+ /* check if this attr is valid with the chosen schema */
+ if (!map[a].name) continue;
+ /* check if it is an attr we are interested in */
+ if (strcasecmp(str, map[a].name) == 0) break;
+ }
+ /* interesting attr */
+ if (a < attrs_num) {
+ store = true;
+ name = map[a].sys_name;
+ } else {
+ store = false;
+ }
+ } else {
+ name = str;
+ store = true;
+ }
+
+ if (store) {
+ vals = ldap_get_values_len(sh->ldap, sm->msg, str);
+ if (!vals) {
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ DEBUG(1, ("LDAP Library error: %d(%s)",
+ lerrno, ldap_err2string(lerrno)));
+ ret = EIO;
+ goto fail;
+ }
+ if (!vals[0]) {
+ DEBUG(1, ("Missing value after ldap_get_values() ??\n"));
+ ret = EINVAL;
+ goto fail;
+ }
+ for (i = 0; vals[i]; i++) {
+ v.data = (uint8_t *)vals[i]->bv_val;
+ v.length = vals[i]->bv_len;
+
+ ret = sysdb_attrs_add_val(attrs, name, &v);
+ if (ret) goto fail;
+ }
+ ldap_value_free_len(vals);
+ }
+
+ ldap_memfree(str);
+ str = ldap_next_attribute(sh->ldap, sm->msg, ber);
+ }
+ ber_free(ber, 0);
+
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ if (lerrno) {
+ DEBUG(1, ("LDAP Library error: %d(%s)",
+ lerrno, ldap_err2string(lerrno)));
+ ret = EIO;
+ goto fail;
+ }
+
+ *_attrs = attrs;
+ return EOK;
+
+fail:
+ if (ber) ber_free(ber, 0);
+ talloc_free(attrs);
+ return ret;
+}
+
+/* This function converts an ldap message into a sysdb_attrs structure.
+ * It converts only known user attributes, the rest are ignored.
+ * If the entry is not that of an user an error is returned.
+ * The original DN is stored as an attribute named originalDN */
+
+int sdap_parse_user(TALLOC_CTX *memctx, struct sdap_options *opts,
+ struct sdap_handle *sh, struct sdap_msg *sm,
+ struct sysdb_attrs **_attrs, char **_dn)
+{
+
+ return sdap_parse_entry(memctx, sh, sm, opts->user_map,
+ SDAP_OPTS_USER, _attrs, _dn);
+}
+
+/* This function converts an ldap message into a sysdb_attrs structure.
+ * It converts only known group attributes, the rest are ignored.
+ * If the entry is not that of an user an error is returned.
+ * The original DN is stored as an attribute named originalDN */
+
+int sdap_parse_group(TALLOC_CTX *memctx, struct sdap_options *opts,
+ struct sdap_handle *sh, struct sdap_msg *sm,
+ struct sysdb_attrs **_attrs, char **_dn)
+{
+
+ return sdap_parse_entry(memctx, sh, sm, opts->group_map,
+ SDAP_OPTS_GROUP, _attrs, _dn);
+}
+
+/* =Get-DN-from-message=================================================== */
+
+int sdap_get_msg_dn(TALLOC_CTX *memctx, struct sdap_handle *sh,
+ struct sdap_msg *sm, char **_dn)
+{
+ char *str;
+ int lerrno;
+
+ lerrno = 0;
+ ldap_set_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+
+ str = ldap_get_dn(sh->ldap, sm->msg);
+ if (!str) {
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ DEBUG(1, ("ldap_get_dn failed: %d(%s)\n",
+ lerrno, ldap_err2string(lerrno)));
+ return EIO;
+ }
+
+ *_dn = talloc_strdup(memctx, str);
+ ldap_memfree(str);
+ if (!*_dn) return ENOMEM;
+
+ return EOK;
+}
+
+errno_t setup_tls_config(struct dp_option *basic_opts)
+{
+ int ret;
+ int ldap_opt_x_tls_require_cert;
+ const char *tls_opt;
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_REQCERT);
+ if (tls_opt) {
+ if (strcasecmp(tls_opt, "never") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_NEVER;
+ }
+ else if (strcasecmp(tls_opt, "allow") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_ALLOW;
+ }
+ else if (strcasecmp(tls_opt, "try") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_TRY;
+ }
+ else if (strcasecmp(tls_opt, "demand") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_DEMAND;
+ }
+ else if (strcasecmp(tls_opt, "hard") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_HARD;
+ }
+ else {
+ DEBUG(1, ("Unknown value for tls_reqcert.\n"));
+ return EINVAL;
+ }
+ /* LDAP_OPT_X_TLS_REQUIRE_CERT has to be set as a global option,
+ * because the SSL/TLS context is initialized from this value. */
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
+ &ldap_opt_x_tls_require_cert);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret)));
+ return EIO;
+ }
+ }
+
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_CACERT);
+ if (tls_opt) {
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, tls_opt);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret)));
+ return EIO;
+ }
+ }
+
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_CACERTDIR);
+ if (tls_opt) {
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR, tls_opt);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret)));
+ return EIO;
+ }
+ }
+
+ return EOK;
+}
+
+
+bool sdap_rootdse_sasl_mech_is_supported(struct sysdb_attrs *rootdse,
+ const char *sasl_mech)
+{
+ struct ldb_message_element *el = NULL;
+ struct ldb_val *val;
+ int i;
+
+ if (!sasl_mech) return false;
+
+ for (i = 0; i < rootdse->num; i++) {
+ if (strcasecmp(rootdse->a[i].name, "supportedSASLMechanisms")) {
+ continue;
+ }
+ el = &rootdse->a[i];
+ break;
+ }
+
+ if (!el) {
+ /* no supported SASL Mechanism at all ? */
+ return false;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ val = &el->values[i];
+ if (strncasecmp(sasl_mech, (const char *)val->data, val->length)) {
+ continue;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+int build_attrs_from_map(TALLOC_CTX *memctx,
+ struct sdap_attr_map *map,
+ size_t size, const char ***_attrs)
+{
+ char **attrs;
+ int i, j;
+
+ attrs = talloc_array(memctx, char *, size + 1);
+ if (!attrs) return ENOMEM;
+
+ /* first attribute is "objectclass" not the specifc one */
+ attrs[0] = talloc_strdup(memctx, "objectClass");
+ if (!attrs[0]) return ENOMEM;
+
+ /* add the others */
+ for (i = j = 1; i < size; i++) {
+ if (map[i].name) {
+ attrs[j] = map[i].name;
+ j++;
+ }
+ }
+ attrs[j] = NULL;
+
+ *_attrs = (const char **)attrs;
+
+ return EOK;
+}
+
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
new file mode 100644
index 000000000..16dbb7843
--- /dev/null
+++ b/src/providers/ldap/sdap.h
@@ -0,0 +1,258 @@
+/*
+ SSSD
+
+ LDAP Helper routines
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com>
+
+ 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/>.
+*/
+
+#ifndef _SDAP_H_
+#define _SDAP_H_
+
+#include "providers/dp_backend.h"
+#include <ldap.h>
+#include "util/sss_ldap.h"
+
+struct sdap_msg {
+ struct sdap_msg *next;
+ LDAPMessage *msg;
+};
+
+struct sdap_op;
+
+typedef void (sdap_op_callback_t)(struct sdap_op *op,
+ struct sdap_msg *, int, void *);
+
+struct sdap_handle;
+
+struct sdap_op {
+ struct sdap_op *prev, *next;
+ struct sdap_handle *sh;
+
+ int msgid;
+ bool done;
+
+ sdap_op_callback_t *callback;
+ void *data;
+
+ struct tevent_context *ev;
+ struct sdap_msg *list;
+ struct sdap_msg *last;
+};
+
+struct fd_event_item {
+ struct fd_event_item *prev;
+ struct fd_event_item *next;
+
+ int fd;
+ struct tevent_fd *fde;
+};
+
+struct ldap_cb_data {
+ struct sdap_handle *sh;
+ struct tevent_context *ev;
+ struct fd_event_item *fd_list;
+};
+
+struct sdap_handle {
+ LDAP *ldap;
+ bool connected;
+
+#ifdef HAVE_LDAP_CONNCB
+ struct ldap_conncb *conncb;
+#else
+ struct tevent_fd *fde;
+#endif
+
+ struct sdap_op *ops;
+};
+
+struct sdap_service {
+ char *name;
+ char *uri;
+};
+
+#define SYSDB_SHADOWPW_LASTCHANGE "shadowLastChange"
+#define SYSDB_SHADOWPW_MIN "shadowMin"
+#define SYSDB_SHADOWPW_MAX "shadowMax"
+#define SYSDB_SHADOWPW_WARNING "shadowWarning"
+#define SYSDB_SHADOWPW_INACTIVE "shadowInactive"
+#define SYSDB_SHADOWPW_EXPIRE "shadowExpire"
+#define SYSDB_SHADOWPW_FLAG "shadowFlag"
+
+#define SYSDB_KRBPW_LASTCHANGE "krbLastPwdChange"
+#define SYSDB_KRBPW_EXPIRATION "krbPasswordExpiration"
+
+#define SYSDB_PWD_ATTRIBUTE "pwdAttribute"
+
+enum sdap_result {
+ SDAP_SUCCESS,
+ SDAP_NOT_FOUND,
+ SDAP_UNAVAIL,
+ SDAP_RETRY,
+ SDAP_ERROR,
+ SDAP_AUTH_SUCCESS,
+ SDAP_AUTH_FAILED,
+ SDAP_AUTH_PW_EXPIRED,
+ SDAP_ACCT_EXPIRED
+};
+
+enum sdap_basic_opt {
+ SDAP_URI = 0,
+ SDAP_SEARCH_BASE,
+ SDAP_DEFAULT_BIND_DN,
+ SDAP_DEFAULT_AUTHTOK_TYPE,
+ SDAP_DEFAULT_AUTHTOK,
+ SDAP_SEARCH_TIMEOUT,
+ SDAP_NETWORK_TIMEOUT,
+ SDAP_OPT_TIMEOUT,
+ SDAP_TLS_REQCERT,
+ SDAP_USER_SEARCH_BASE,
+ SDAP_USER_SEARCH_SCOPE,
+ SDAP_USER_SEARCH_FILTER,
+ SDAP_GROUP_SEARCH_BASE,
+ SDAP_GROUP_SEARCH_SCOPE,
+ SDAP_GROUP_SEARCH_FILTER,
+ SDAP_SCHEMA,
+ SDAP_OFFLINE_TIMEOUT,
+ SDAP_FORCE_UPPER_CASE_REALM,
+ SDAP_ENUM_REFRESH_TIMEOUT,
+ SDAP_CACHE_PURGE_TIMEOUT,
+ SDAP_ENTRY_CACHE_TIMEOUT,
+ SDAP_TLS_CACERT,
+ SDAP_TLS_CACERTDIR,
+ SDAP_ID_TLS,
+ SDAP_SASL_MECH,
+ SDAP_SASL_AUTHID,
+ SDAP_KRB5_KEYTAB,
+ SDAP_KRB5_KINIT,
+ SDAP_KRB5_REALM,
+ SDAP_PWD_POLICY,
+ SDAP_REFERRALS,
+
+ SDAP_OPTS_BASIC /* opts counter */
+};
+
+enum sdap_gen_attrs {
+ SDAP_AT_ENTRY_USN = 0,
+ SDAP_AT_LAST_USN,
+
+ SDAP_AT_GENERAL /* attrs counter */
+};
+
+/* the objectclass must be the first attribute.
+ * Functions depend on this */
+enum sdap_user_attrs {
+ SDAP_OC_USER = 0,
+ SDAP_AT_USER_NAME,
+ SDAP_AT_USER_PWD,
+ SDAP_AT_USER_UID,
+ SDAP_AT_USER_GID,
+ SDAP_AT_USER_GECOS,
+ SDAP_AT_USER_HOME,
+ SDAP_AT_USER_SHELL,
+ SDAP_AT_USER_PRINC,
+ SDAP_AT_USER_FULLNAME,
+ SDAP_AT_USER_MEMBEROF,
+ SDAP_AT_USER_UUID,
+ SDAP_AT_USER_MODSTAMP,
+ SDAP_AT_SP_LSTCHG,
+ SDAP_AT_SP_MIN,
+ SDAP_AT_SP_MAX,
+ SDAP_AT_SP_WARN,
+ SDAP_AT_SP_INACT,
+ SDAP_AT_SP_EXPIRE,
+ SDAP_AT_SP_FLAG,
+ SDAP_AT_KP_LASTCHANGE,
+ SDAP_AT_KP_EXPIRATION,
+ SDAP_AT_PWD_ATTRIBUTE,
+
+ SDAP_OPTS_USER /* attrs counter */
+};
+
+#define SDAP_FIRST_EXTRA_USER_AT SDAP_AT_SP_LSTCHG
+
+/* the objectclass must be the first attribute.
+ * Functions depend on this */
+enum sdap_group_attrs {
+ SDAP_OC_GROUP = 0,
+ SDAP_AT_GROUP_NAME,
+ SDAP_AT_GROUP_PWD,
+ SDAP_AT_GROUP_GID,
+ SDAP_AT_GROUP_MEMBER,
+ SDAP_AT_GROUP_UUID,
+ SDAP_AT_GROUP_MODSTAMP,
+
+ SDAP_OPTS_GROUP /* attrs counter */
+};
+
+struct sdap_attr_map {
+ const char *opt_name;
+ const char *def_name;
+ const char *sys_name;
+ char *name;
+};
+
+struct sdap_options {
+ struct dp_option *basic;
+ struct sdap_attr_map *gen_map;
+ struct sdap_attr_map *user_map;
+ struct sdap_attr_map *group_map;
+
+ /* supported schema types */
+ enum schema_type {
+ SDAP_SCHEMA_RFC2307 = 1, /* memberUid = uid */
+ SDAP_SCHEMA_RFC2307BIS = 2, /* member = dn */
+ SDAP_SCHEMA_IPA_V1 = 3, /* member/memberof */
+ SDAP_SCHEMA_AD = 4 /* AD's member/memberof */
+ } schema_type;
+
+ struct ldb_dn *users_base;
+ struct ldb_dn *groups_base;
+};
+
+int sdap_get_map(TALLOC_CTX *memctx,
+ struct confdb_ctx *cdb,
+ const char *conf_path,
+ struct sdap_attr_map *def_map,
+ int num_entries,
+ struct sdap_attr_map **_map);
+
+int sdap_parse_entry(TALLOC_CTX *memctx,
+ struct sdap_handle *sh, struct sdap_msg *sm,
+ struct sdap_attr_map *map, int attrs_num,
+ struct sysdb_attrs **_attrs, char **_dn);
+
+int sdap_parse_user(TALLOC_CTX *memctx, struct sdap_options *opts,
+ struct sdap_handle *sh, struct sdap_msg *sm,
+ struct sysdb_attrs **_attrs, char **_dn);
+
+int sdap_parse_group(TALLOC_CTX *memctx, struct sdap_options *opts,
+ struct sdap_handle *sh, struct sdap_msg *sm,
+ struct sysdb_attrs **_attrs, char **_dn);
+
+int sdap_get_msg_dn(TALLOC_CTX *memctx, struct sdap_handle *sh,
+ struct sdap_msg *sm, char **_dn);
+
+errno_t setup_tls_config(struct dp_option *basic_opts);
+
+bool sdap_rootdse_sasl_mech_is_supported(struct sysdb_attrs *rootdse,
+ const char *sasl_mech);
+
+int build_attrs_from_map(TALLOC_CTX *memctx,
+ struct sdap_attr_map *map,
+ size_t size, const char ***_attrs);
+#endif /* _SDAP_H_ */
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
new file mode 100644
index 000000000..959c08a65
--- /dev/null
+++ b/src/providers/ldap/sdap_async.c
@@ -0,0 +1,1018 @@
+/*
+ SSSD
+
+ Async LDAP Helper routines
+
+ Copyright (C) Simo Sorce <ssorce@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 <ctype.h>
+#include "util/util.h"
+#include "providers/ldap/sdap_async_private.h"
+
+#define REALM_SEPARATOR '@'
+#define REPLY_REALLOC_INCREMENT 10
+
+void make_realm_upper_case(const char *upn)
+{
+ char *c;
+
+ c = strchr(upn, REALM_SEPARATOR);
+ if (c == NULL) {
+ DEBUG(9, ("No realm delimiter found in upn [%s].\n", upn));
+ return;
+ }
+
+ while(*(++c) != '\0') {
+ c[0] = toupper(*c);
+ }
+
+ return;
+}
+
+/* ==LDAP-Memory-Handling================================================= */
+
+static int lmsg_destructor(void *mem)
+{
+ ldap_msgfree((LDAPMessage *)mem);
+ return 0;
+}
+
+static int sdap_msg_attach(TALLOC_CTX *memctx, LDAPMessage *msg)
+{
+ void *h;
+
+ if (!msg) return EINVAL;
+
+ h = sss_mem_attach(memctx, msg, lmsg_destructor);
+ if (!h) return ENOMEM;
+
+ return EOK;
+}
+
+/* ==sdap-hanlde-utility-functions======================================== */
+
+static inline void sdap_handle_release(struct sdap_handle *sh);
+static int sdap_handle_destructor(void *mem);
+
+struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx)
+{
+ struct sdap_handle *sh;
+
+ sh = talloc_zero(memctx, struct sdap_handle);
+ if (!sh) return NULL;
+
+ talloc_set_destructor((TALLOC_CTX *)sh, sdap_handle_destructor);
+
+ return sh;
+}
+
+static int sdap_handle_destructor(void *mem)
+{
+ struct sdap_handle *sh = talloc_get_type(mem, struct sdap_handle);
+
+ sdap_handle_release(sh);
+
+ return 0;
+}
+
+static void sdap_handle_release(struct sdap_handle *sh)
+{
+ DEBUG(8, ("Trace: sh[%p], connected[%d], ops[%p], ldap[%p]\n",
+ sh, (int)sh->connected, sh->ops, sh->ldap));
+
+ if (sh->connected) {
+ struct sdap_op *op;
+
+#ifdef HAVE_LDAP_CONNCB
+ /* remove all related fd events from the event loop */
+ talloc_zfree(sh->conncb->lc_arg);
+#else
+ talloc_zfree(sh->fde);
+#endif
+
+ while (sh->ops) {
+ op = sh->ops;
+ op->callback(op, NULL, EIO, op->data);
+ /* calling the callback may result in freeing the op */
+ /* check if it is still the same or avoid freeing */
+ if (op == sh->ops) talloc_free(op);
+ }
+
+ if (sh->ldap) {
+ ldap_unbind_ext(sh->ldap, NULL, NULL);
+ }
+#ifdef HAVE_LDAP_CONNCB
+ talloc_zfree(sh->conncb);
+#endif
+ sh->connected = false;
+ sh->ldap = NULL;
+ sh->ops = NULL;
+ }
+}
+
+/* ==Parse-Results-And-Handle-Disconnections============================== */
+static void sdap_process_message(struct tevent_context *ev,
+ struct sdap_handle *sh, LDAPMessage *msg);
+static void sdap_process_result(struct tevent_context *ev, void *pvt);
+static void sdap_process_next_reply(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt);
+
+static void sdap_ldap_result(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *pvt)
+{
+ sdap_process_result(ev, pvt);
+}
+
+static void sdap_ldap_next_result(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ sdap_process_result(ev, pvt);
+}
+
+static void sdap_process_result(struct tevent_context *ev, void *pvt)
+{
+ struct sdap_handle *sh = talloc_get_type(pvt, struct sdap_handle);
+ struct timeval no_timeout = {0, 0};
+ struct tevent_timer *te;
+ LDAPMessage *msg;
+ int ret;
+
+ DEBUG(8, ("Trace: sh[%p], connected[%d], ops[%p], ldap[%p]\n",
+ sh, (int)sh->connected, sh->ops, sh->ldap));
+
+ if (!sh->connected || !sh->ldap) {
+ DEBUG(2, ("ERROR: LDAP connection is not connected!\n"));
+ return;
+ }
+
+ ret = ldap_result(sh->ldap, LDAP_RES_ANY, 0, &no_timeout, &msg);
+ if (ret == 0) {
+ /* this almost always means we have reached the end of
+ * the list of received messages */
+ DEBUG(8, ("Trace: ldap_result found nothing!\n"));
+ return;
+ }
+
+ if (ret == -1) {
+ DEBUG(4, ("ldap_result gave -1, something bad happend!\n"));
+ sdap_handle_release(sh);
+ return;
+ }
+
+ /* We don't know if this will be the last result.
+ *
+ * important: we must do this before actually processing the message
+ * because the message processing might even free the sdap_handler
+ * so it must be the last operation.
+ * FIXME: use tevent_immediate/tevent_queues, when avilable */
+ memset(&no_timeout, 0, sizeof(struct timeval));
+
+ te = tevent_add_timer(ev, sh, no_timeout, sdap_ldap_next_result, sh);
+ if (!te) {
+ DEBUG(1, ("Failed to add critical timer to fetch next result!\n"));
+ }
+
+ /* now process this message */
+ sdap_process_message(ev, sh, msg);
+}
+
+/* process a messgae calling the right operation callback.
+ * msg is completely taken care of (including freeeing it)
+ * NOTE: this function may even end up freeing the sdap_handle
+ * so sdap_hanbdle must not be used after this function is called
+ */
+static void sdap_process_message(struct tevent_context *ev,
+ struct sdap_handle *sh, LDAPMessage *msg)
+{
+ struct sdap_msg *reply;
+ struct sdap_op *op;
+ int msgid;
+ int msgtype;
+ int ret;
+
+ msgid = ldap_msgid(msg);
+ if (msgid == -1) {
+ DEBUG(2, ("can't fire callback, message id invalid!\n"));
+ ldap_msgfree(msg);
+ return;
+ }
+
+ msgtype = ldap_msgtype(msg);
+
+ for (op = sh->ops; op; op = op->next) {
+ if (op->msgid == msgid) break;
+ }
+
+ if (op == NULL) {
+ DEBUG(2, ("Unmatched msgid, discarding message (type: %0x)\n",
+ msgtype));
+ ldap_msgfree(msg);
+ return;
+ }
+
+ /* shouldn't happen */
+ if (op->done) {
+ DEBUG(2, ("Operation [%p] already handled (type: %0x)\n", op, msgtype));
+ ldap_msgfree(msg);
+ return;
+ }
+
+ switch (msgtype) {
+ case LDAP_RES_SEARCH_ENTRY:
+ /* go and process entry */
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ /* more ops to come with this msgid */
+ /* just ignore */
+ ldap_msgfree(msg);
+ return;
+
+ case LDAP_RES_BIND:
+ case LDAP_RES_SEARCH_RESULT:
+ case LDAP_RES_MODIFY:
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ case LDAP_RES_MODDN:
+ case LDAP_RES_COMPARE:
+ case LDAP_RES_EXTENDED:
+ case LDAP_RES_INTERMEDIATE:
+ /* no more results expected with this msgid */
+ op->done = true;
+ break;
+
+ default:
+ /* unkwon msg type ?? */
+ DEBUG(1, ("Couldn't figure out the msg type! [%0x]\n", msgtype));
+ ldap_msgfree(msg);
+ return;
+ }
+
+ reply = talloc_zero(op, struct sdap_msg);
+ if (!reply) {
+ ldap_msgfree(msg);
+ ret = ENOMEM;
+ } else {
+ reply->msg = msg;
+ ret = sdap_msg_attach(reply, msg);
+ if (ret != EOK) {
+ ldap_msgfree(msg);
+ talloc_zfree(reply);
+ }
+ }
+
+ if (op->list) {
+ /* list exist, queue it */
+
+ op->last->next = reply;
+ op->last = reply;
+
+ } else {
+ /* create list, then call callback */
+ op->list = op->last = reply;
+
+ /* must be the last operation as it may end up freeing all memory
+ * including all ops handlers */
+ op->callback(op, reply, ret, op->data);
+ }
+}
+
+static void sdap_unlock_next_reply(struct sdap_op *op)
+{
+ struct timeval tv;
+ struct tevent_timer *te;
+ struct sdap_msg *next_reply;
+
+ if (op->list) {
+ next_reply = op->list->next;
+ /* get rid of the previous reply, it has been processed already */
+ talloc_zfree(op->list);
+ op->list = next_reply;
+ }
+
+ /* if there are still replies to parse, queue a new operation */
+ if (op->list) {
+ /* use a very small timeout, so that fd operations have a chance to be
+ * served while processing a long reply */
+ tv = tevent_timeval_current();
+
+ /* wait 5 microsecond */
+ tv.tv_usec += 5;
+ tv.tv_sec += tv.tv_usec / 1000000;
+ tv.tv_usec = tv.tv_usec % 1000000;
+
+ te = tevent_add_timer(op->ev, op, tv,
+ sdap_process_next_reply, op);
+ if (!te) {
+ DEBUG(1, ("Failed to add critical timer for next reply!\n"));
+ op->callback(op, NULL, EFAULT, op->data);
+ }
+ }
+}
+
+static void sdap_process_next_reply(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct sdap_op *op = talloc_get_type(pvt, struct sdap_op);
+
+ op->callback(op, op->list, EOK, op->data);
+}
+
+#ifdef HAVE_LDAP_CONNCB
+int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv,
+ struct sockaddr *addr, struct ldap_conncb *ctx)
+{
+ int ret;
+ ber_socket_t ber_fd;
+ struct fd_event_item *fd_event_item;
+ struct ldap_cb_data *cb_data = talloc_get_type(ctx->lc_arg,
+ struct ldap_cb_data);
+
+ ret = ber_sockbuf_ctrl(sb, LBER_SB_OPT_GET_FD, &ber_fd);
+ if (ret == -1) {
+ DEBUG(1, ("ber_sockbuf_ctrl failed.\n"));
+ return EINVAL;
+ }
+ DEBUG(9, ("New LDAP connection to [%s] with fd [%d].\n",
+ ldap_url_desc2str(srv), ber_fd));
+
+ fd_event_item = talloc_zero(cb_data, struct fd_event_item);
+ if (fd_event_item == NULL) {
+ DEBUG(1, ("talloc failed.\n"));
+ return ENOMEM;
+ }
+
+ fd_event_item->fde = tevent_add_fd(cb_data->ev, fd_event_item, ber_fd,
+ TEVENT_FD_READ, sdap_ldap_result,
+ cb_data->sh);
+ if (fd_event_item->fde == NULL) {
+ DEBUG(1, ("tevent_add_fd failed.\n"));
+ talloc_free(fd_event_item);
+ return ENOMEM;
+ }
+ fd_event_item->fd = ber_fd;
+
+ DLIST_ADD(cb_data->fd_list, fd_event_item);
+
+ return LDAP_SUCCESS;
+}
+
+void sdap_ldap_connect_callback_del(LDAP *ld, Sockbuf *sb,
+ struct ldap_conncb *ctx)
+{
+ int ret;
+ ber_socket_t ber_fd;
+ struct fd_event_item *fd_event_item;
+ struct ldap_cb_data *cb_data = talloc_get_type(ctx->lc_arg,
+ struct ldap_cb_data);
+
+ if (sb == NULL || cb_data == NULL) {
+ return;
+ }
+
+ ret = ber_sockbuf_ctrl(sb, LBER_SB_OPT_GET_FD, &ber_fd);
+ if (ret == -1) {
+ DEBUG(1, ("ber_sockbuf_ctrl failed.\n"));
+ return;
+ }
+ DEBUG(9, ("Closing LDAP connection with fd [%d].\n", ber_fd));
+
+ DLIST_FOR_EACH(fd_event_item, cb_data->fd_list) {
+ if (fd_event_item->fd == ber_fd) {
+ break;
+ }
+ }
+ if (fd_event_item == NULL) {
+ DEBUG(1, ("No event for fd [%d] found.\n", ber_fd));
+ return;
+ }
+
+ DLIST_REMOVE(cb_data->fd_list, fd_event_item);
+ talloc_zfree(fd_event_item);
+
+ return;
+}
+
+#else
+
+static int get_fd_from_ldap(LDAP *ldap, int *fd)
+{
+ int ret;
+
+ ret = ldap_get_option(ldap, LDAP_OPT_DESC, fd);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("Failed to get fd from ldap!!\n"));
+ *fd = -1;
+ return EIO;
+ }
+
+ return EOK;
+}
+
+int sdap_install_ldap_callbacks(struct sdap_handle *sh,
+ struct tevent_context *ev)
+{
+ int fd;
+ int ret;
+
+ ret = get_fd_from_ldap(sh->ldap, &fd);
+ if (ret) return ret;
+
+ sh->fde = tevent_add_fd(ev, sh, fd, TEVENT_FD_READ, sdap_ldap_result, sh);
+ if (!sh->fde) return ENOMEM;
+
+ DEBUG(8, ("Trace: sh[%p], connected[%d], ops[%p], fde[%p], ldap[%p]\n",
+ sh, (int)sh->connected, sh->ops, sh->fde, sh->ldap));
+
+ return EOK;
+}
+
+#endif
+
+
+/* ==LDAP-Operations-Helpers============================================== */
+
+static int sdap_op_destructor(void *mem)
+{
+ struct sdap_op *op = (struct sdap_op *)mem;
+
+ DLIST_REMOVE(op->sh->ops, op);
+
+ if (op->done) return 0;
+
+ /* we don't check the result here, if a message was really abandoned,
+ * hopefully the server will get an abandon.
+ * If the operation was already fully completed, this is going to be
+ * just a noop */
+ ldap_abandon_ext(op->sh->ldap, op->msgid, NULL, NULL);
+
+ return 0;
+}
+
+static void sdap_op_timeout(struct tevent_req *req)
+{
+ struct sdap_op *op = tevent_req_callback_data(req, struct sdap_op);
+
+ /* should never happen, but just in case */
+ if (op->done) {
+ DEBUG(2, ("Timeout happened after op was finished !?\n"));
+ return;
+ }
+
+ /* signal the caller that we have a timeout */
+ op->callback(op, NULL, ETIMEDOUT, op->data);
+}
+
+int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev,
+ struct sdap_handle *sh, int msgid,
+ sdap_op_callback_t *callback, void *data,
+ int timeout, struct sdap_op **_op)
+{
+ struct sdap_op *op;
+
+ op = talloc_zero(memctx, struct sdap_op);
+ if (!op) return ENOMEM;
+
+ op->sh = sh;
+ op->msgid = msgid;
+ op->callback = callback;
+ op->data = data;
+ op->ev = ev;
+
+ /* check if we need to set a timeout */
+ if (timeout) {
+ struct tevent_req *req;
+ struct timeval tv;
+
+ tv = tevent_timeval_current();
+ tv = tevent_timeval_add(&tv, timeout, 0);
+
+ /* allocate on op, so when it get freed the timeout is removed */
+ req = tevent_wakeup_send(op, ev, tv);
+ if (!req) {
+ talloc_zfree(op);
+ return ENOMEM;
+ }
+ tevent_req_set_callback(req, sdap_op_timeout, op);
+ }
+
+ DLIST_ADD(sh->ops, op);
+
+ talloc_set_destructor((TALLOC_CTX *)op, sdap_op_destructor);
+
+ *_op = op;
+ return EOK;
+}
+
+/* ==Modify-Password====================================================== */
+
+struct sdap_exop_modify_passwd_state {
+ struct sdap_handle *sh;
+
+ struct sdap_op *op;
+
+ int result;
+ char *user_error_message;
+};
+
+static void sdap_exop_modify_passwd_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt);
+
+struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ char *user_dn,
+ char *password,
+ char *new_password)
+{
+ struct tevent_req *req = NULL;
+ struct sdap_exop_modify_passwd_state *state;
+ int ret;
+ BerElement *ber = NULL;
+ struct berval *bv = NULL;
+ int msgid;
+ LDAPControl *request_controls[2];
+
+ req = tevent_req_create(memctx, &state,
+ struct sdap_exop_modify_passwd_state);
+ if (!req) return NULL;
+
+ state->sh = sh;
+ state->user_error_message = NULL;
+
+ ber = ber_alloc_t( LBER_USE_DER );
+ if (ber == NULL) {
+ DEBUG(7, ("ber_alloc_t failed.\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ ret = ber_printf( ber, "{tststs}", LDAP_TAG_EXOP_MODIFY_PASSWD_ID,
+ user_dn,
+ LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, password,
+ LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, new_password);
+ if (ret == -1) {
+ DEBUG(1, ("ber_printf failed.\n"));
+ ber_free(ber, 1);
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ ret = ber_flatten(ber, &bv);
+ ber_free(ber, 1);
+ if (ret == -1) {
+ DEBUG(1, ("ber_flatten failed.\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ 0, NULL, 0, &request_controls[0]);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(1, ("sss_ldap_control_create failed.\n"));
+ goto fail;
+ }
+ request_controls[1] = NULL;
+
+ DEBUG(4, ("Executing extended operation\n"));
+
+ ret = ldap_extended_operation(state->sh->ldap, LDAP_EXOP_MODIFY_PASSWD,
+ bv, request_controls, NULL, &msgid);
+ ber_bvfree(bv);
+ ldap_control_free(request_controls[0]);
+ if (ret == -1 || msgid == -1) {
+ DEBUG(1, ("ldap_extended_operation failed\n"));
+ goto fail;
+ }
+ DEBUG(8, ("ldap_extended_operation sent, msgid = %d\n", msgid));
+
+ /* FIXME: get timeouts from configuration, for now 5 secs. */
+ ret = sdap_op_add(state, ev, state->sh, msgid,
+ sdap_exop_modify_passwd_done, req, 5, &state->op);
+ if (ret) {
+ DEBUG(1, ("Failed to set up operation!\n"));
+ goto fail;
+ }
+
+ return req;
+
+fail:
+ tevent_req_error(req, EIO);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_exop_modify_passwd_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct sdap_exop_modify_passwd_state *state = tevent_req_data(req,
+ struct sdap_exop_modify_passwd_state);
+ char *errmsg = NULL;
+ int ret;
+ LDAPControl **response_controls = NULL;
+ int c;
+ ber_int_t pp_grace;
+ ber_int_t pp_expire;
+ LDAPPasswordPolicyError pp_error;
+
+ if (error) {
+ tevent_req_error(req, error);
+ return;
+ }
+
+ ret = ldap_parse_result(state->sh->ldap, reply->msg,
+ &state->result, NULL, &errmsg, NULL,
+ &response_controls, 0);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid));
+ ret = EIO;
+ goto done;
+ }
+
+ if (response_controls == NULL) {
+ DEBUG(5, ("Server returned no controls.\n"));
+ } else {
+ for (c = 0; response_controls[c] != NULL; c++) {
+ DEBUG(9, ("Server returned control [%s].\n",
+ response_controls[c]->ldctl_oid));
+ if (strcmp(response_controls[c]->ldctl_oid,
+ LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) {
+ ret = ldap_parse_passwordpolicy_control(state->sh->ldap,
+ response_controls[c],
+ &pp_expire, &pp_grace,
+ &pp_error);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(1, ("ldap_parse_passwordpolicy_control failed.\n"));
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(7, ("Password Policy Response: expire [%d] grace [%d] "
+ "error [%s].\n", pp_expire, pp_grace,
+ ldap_passwordpolicy_err2txt(pp_error)));
+ }
+ }
+ }
+
+ if (state->result != LDAP_SUCCESS) {
+ state->user_error_message = talloc_strdup(state, errmsg);
+ if (state->user_error_message == NULL) {
+ DEBUG(1, ("talloc_strdup failed.\n"));
+ }
+ }
+
+ DEBUG(3, ("ldap_extended_operation result: %s(%d), %s\n",
+ ldap_err2string(state->result), state->result, errmsg));
+
+ ret = LDAP_SUCCESS;
+done:
+ ldap_controls_free(response_controls);
+ ldap_memfree(errmsg);
+
+ if (ret == LDAP_SUCCESS) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+}
+
+int sdap_exop_modify_passwd_recv(struct tevent_req *req,
+ TALLOC_CTX * mem_ctx,
+ enum sdap_result *result,
+ char **user_error_message)
+{
+ struct sdap_exop_modify_passwd_state *state = tevent_req_data(req,
+ struct sdap_exop_modify_passwd_state);
+
+ *result = SDAP_ERROR;
+ *user_error_message = talloc_steal(mem_ctx, state->user_error_message);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (state->result == LDAP_SUCCESS) {
+ *result = SDAP_SUCCESS;
+ }
+
+ return EOK;
+}
+
+/* ==Fetch-RootDSE============================================= */
+
+struct sdap_get_rootdse_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+ struct sdap_handle *sh;
+
+ struct sysdb_attrs *rootdse;
+};
+
+static void sdap_get_rootdse_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct sdap_handle *sh)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_get_rootdse_state *state;
+
+ DEBUG(9, ("Getting rootdse\n"));
+
+ req = tevent_req_create(memctx, &state, struct sdap_get_rootdse_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sh = sh;
+ state->rootdse = NULL;
+
+ subreq = sdap_get_generic_send(state, ev, opts, sh,
+ "", LDAP_SCOPE_BASE,
+ "(objectclass=*)", NULL, NULL, 0);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_get_rootdse_done, req);
+
+ return req;
+}
+
+static void sdap_get_rootdse_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_rootdse_state *state = tevent_req_data(req,
+ struct sdap_get_rootdse_state);
+ struct sysdb_attrs **results;
+ size_t num_results;
+ int ret;
+
+ ret = sdap_get_generic_recv(subreq, state, &num_results, &results);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (num_results == 0 || !results) {
+ DEBUG(2, ("No RootDSE for server ?!\n"));
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+
+ if (num_results > 1) {
+ DEBUG(2, ("Multiple replies when searching for RootDSE ??\n"));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ state->rootdse = talloc_steal(state, results[0]);
+ talloc_zfree(results);
+
+ DEBUG(9, ("Got rootdse\n"));
+
+ tevent_req_done(req);
+}
+
+int sdap_get_rootdse_recv(struct tevent_req *req,
+ TALLOC_CTX *memctx,
+ struct sysdb_attrs **rootdse)
+{
+ struct sdap_get_rootdse_state *state = tevent_req_data(req,
+ struct sdap_get_rootdse_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *rootdse = talloc_steal(memctx, state->rootdse);
+
+ return EOK;
+}
+
+/* ==Generic Search============================================ */
+
+struct sdap_get_generic_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+ struct sdap_handle *sh;
+ const char *search_base;
+ int scope;
+ const char *filter;
+ const char **attrs;
+ struct sdap_attr_map *map;
+ int map_num_attrs;
+
+ struct sdap_op *op;
+
+ size_t reply_max;
+ size_t reply_count;
+ struct sysdb_attrs **reply;
+};
+
+static errno_t add_to_reply(struct sdap_get_generic_state *state,
+ struct sysdb_attrs *msg);
+
+static void sdap_get_generic_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt);
+
+struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ const char *search_base,
+ int scope,
+ const char *filter,
+ const char **attrs,
+ struct sdap_attr_map *map,
+ int map_num_attrs)
+{
+ struct tevent_req *req = NULL;
+ struct sdap_get_generic_state *state = NULL;
+ int lret;
+ int ret;
+ int msgid;
+
+ req = tevent_req_create(memctx, &state, struct sdap_get_generic_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sh = sh;
+ state->search_base = search_base;
+ state->scope = scope;
+ state->filter = filter;
+ state->attrs = attrs;
+ state->map = map;
+ state->map_num_attrs = map_num_attrs;
+ state->op = NULL;
+ state->reply_max = 0;
+ state->reply_count = 0;
+ state->reply = NULL;
+
+ DEBUG(6, ("calling ldap_search_ext with [%s][%s].\n", state->filter,
+ state->search_base));
+ if (debug_level >= 7) {
+ int i;
+
+ if (state->attrs) {
+ for (i = 0; state->attrs[i]; i++) {
+ DEBUG(7, ("Requesting attrs: [%s]\n", state->attrs[i]));
+ }
+ }
+ }
+
+ lret = ldap_search_ext(state->sh->ldap, state->search_base,
+ state->scope, state->filter,
+ discard_const(state->attrs),
+ false, NULL, NULL, NULL, 0, &msgid);
+ if (lret != LDAP_SUCCESS) {
+ DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(lret)));
+ ret = EIO;
+ goto fail;
+ }
+ DEBUG(8, ("ldap_search_ext called, msgid = %d\n", msgid));
+
+ ret = sdap_op_add(state, state->ev, state->sh, msgid,
+ sdap_get_generic_done, req,
+ dp_opt_get_int(state->opts->basic,
+ SDAP_SEARCH_TIMEOUT),
+ &state->op);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to set up operation!\n"));
+ goto fail;
+ }
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+
+static void sdap_get_generic_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct sdap_get_generic_state *state = tevent_req_data(req,
+ struct sdap_get_generic_state);
+ struct sysdb_attrs *attrs;
+ char *errmsg;
+ int result;
+ int ret;
+
+ if (error) {
+ tevent_req_error(req, error);
+ return;
+ }
+
+ switch (ldap_msgtype(reply->msg)) {
+ case LDAP_RES_SEARCH_REFERENCE:
+ /* ignore references for now */
+ talloc_free(reply);
+
+ /* unlock the operation so that we can proceed with the next result */
+ sdap_unlock_next_reply(state->op);
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ ret = sdap_parse_entry(state, state->sh, reply,
+ state->map, state->map_num_attrs,
+ &attrs, NULL);
+ if (ret != EOK) {
+ DEBUG(1, ("sdap_parse_generic_entry failed.\n"));
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ ret = add_to_reply(state, attrs);
+ if (ret != EOK) {
+ DEBUG(1, ("add_to_reply failed.\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sdap_unlock_next_reply(state->op);
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ ret = ldap_parse_result(state->sh->ldap, reply->msg,
+ &result, NULL, &errmsg, NULL, NULL, 0);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ DEBUG(6, ("Search result: %s(%d), %s\n",
+ ldap_err2string(result), result, errmsg));
+
+ tevent_req_done(req);
+ return;
+
+ default:
+ /* what is going on here !? */
+ tevent_req_error(req, EIO);
+ return;
+ }
+}
+
+static errno_t add_to_reply(struct sdap_get_generic_state *state,
+ struct sysdb_attrs *msg)
+{
+ if (state->reply == NULL || state->reply_max == state->reply_count) {
+ state->reply_max += REPLY_REALLOC_INCREMENT;
+ state->reply = talloc_realloc(state, state->reply,
+ struct sysdb_attrs *,
+ state->reply_max);
+ if (state->reply == NULL) {
+ DEBUG(1, ("talloc_realloc failed.\n"));
+ return ENOMEM;
+ }
+ }
+
+ state->reply[state->reply_count++] = talloc_steal(state->reply, msg);
+
+ return EOK;
+}
+
+int sdap_get_generic_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *reply_count,
+ struct sysdb_attrs ***reply)
+{
+ struct sdap_get_generic_state *state = tevent_req_data(req,
+ struct sdap_get_generic_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *reply_count = state->reply_count;
+ *reply = talloc_steal(mem_ctx, state->reply);
+
+ return EOK;
+}
+
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
new file mode 100644
index 000000000..3c52d236b
--- /dev/null
+++ b/src/providers/ldap/sdap_async.h
@@ -0,0 +1,126 @@
+/*
+ SSSD
+
+ Async LDAP Helper routines
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com>
+
+ 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/>.
+*/
+
+#ifndef _SDAP_ASYNC_H_
+#define _SDAP_ASYNC_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include "providers/dp_backend.h"
+#include "providers/ldap/sdap.h"
+#include "providers/fail_over.h"
+
+struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ const char *uri,
+ bool use_start_tls);
+int sdap_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *memctx,
+ struct sdap_handle **sh);
+
+struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ const char **attrs,
+ const char *wildcard);
+int sdap_get_users_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, char **timestamp);
+
+struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ const char **attrs,
+ const char *wildcard);
+int sdap_get_groups_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, char **timestamp);
+
+struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ int timeout,
+ const char *keytab,
+ const char *principal,
+ const char *realm);
+int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result);
+
+struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ const char *sasl_mech,
+ const char *sasl_user,
+ const char *user_dn,
+ const char *authtok_type,
+ struct dp_opt_blob authtok);
+int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result);
+
+struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ const char *name,
+ const char **grp_attrs);
+int sdap_get_initgr_recv(struct tevent_req *req);
+
+struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ char *user_dn,
+ char *password,
+ char *new_password);
+int sdap_exop_modify_passwd_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ enum sdap_result *result,
+ char **user_error_msg);
+
+struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct be_ctx *be,
+ struct sdap_service *service,
+ struct sysdb_attrs **rootdse);
+int sdap_cli_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *memctx,
+ struct sdap_handle **gsh,
+ struct sysdb_attrs **rootdse);
+
+struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ const char *search_base,
+ int scope,
+ const char *filter,
+ const char **attrs,
+ struct sdap_attr_map *map,
+ int map_num_attrs);
+int sdap_get_generic_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, size_t *reply_count,
+ struct sysdb_attrs ***reply_list);
+
+#endif /* _SDAP_ASYNC_H_ */
diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
new file mode 100644
index 000000000..abebe2883
--- /dev/null
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -0,0 +1,2065 @@
+/*
+ SSSD
+
+ Async LDAP Helper routines
+
+ Copyright (C) Simo Sorce <ssorce@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 "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ldap/sdap_async_private.h"
+
+/* ==Save-User-Entry====================================================== */
+
+struct sdap_save_user_state {
+ struct tevent_context *ev;
+ struct sysdb_handle *handle;
+ struct sdap_options *opts;
+
+ struct sss_domain_info *dom;
+
+ const char *name;
+ struct sysdb_attrs *attrs;
+ char *timestamp;
+};
+
+static void sdap_save_user_done(struct tevent_req *subreq);
+
+ /* FIXME: support storing additional attributes */
+
+static struct tevent_req *sdap_save_user_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct sdap_options *opts,
+ struct sss_domain_info *dom,
+ struct sysdb_attrs *attrs,
+ bool is_initgr)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_save_user_state *state;
+ struct ldb_message_element *el;
+ int ret;
+ const char *pwd;
+ const char *gecos;
+ const char *homedir;
+ const char *shell;
+ long int l;
+ uid_t uid;
+ gid_t gid;
+ struct sysdb_attrs *user_attrs;
+ char *upn = NULL;
+ int i;
+ char *val = NULL;
+ int cache_timeout;
+
+ DEBUG(9, ("Save user\n"));
+
+ req = tevent_req_create(memctx, &state, struct sdap_save_user_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->handle = handle;
+ state->dom = dom;
+ state->opts = opts;
+ state->attrs = attrs;
+ state->timestamp = NULL;
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_NAME].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) {
+ ret = EINVAL;
+ goto fail;
+ }
+ state->name = (const char *)el->values[0].data;
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_PWD].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) pwd = NULL;
+ else pwd = (const char *)el->values[0].data;
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_GECOS].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) gecos = NULL;
+ else gecos = (const char *)el->values[0].data;
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_HOME].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) homedir = NULL;
+ else homedir = (const char *)el->values[0].data;
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_SHELL].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) shell = NULL;
+ else shell = (const char *)el->values[0].data;
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_UID].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) {
+ DEBUG(1, ("no uid provided for [%s] in domain [%s].\n",
+ state->name, dom->name));
+ ret = EINVAL;
+ goto fail;
+ }
+ errno = 0;
+ l = strtol((const char *)el->values[0].data, NULL, 0);
+ if (errno) {
+ ret = EINVAL;
+ goto fail;
+ }
+ uid = l;
+
+ /* check that the uid is valid for this domain */
+ if (OUT_OF_ID_RANGE(uid, dom->id_min, dom->id_max)) {
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ state->name));
+ ret = EINVAL;
+ goto fail;
+ }
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_GID].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) {
+ DEBUG(1, ("no gid provided for [%s] in domain [%s].\n",
+ state->name, dom->name));
+ ret = EINVAL;
+ goto fail;
+ }
+ errno = 0;
+ l = strtol((const char *)el->values[0].data, NULL, 0);
+ if (errno) {
+ ret = EINVAL;
+ goto fail;
+ }
+ gid = l;
+
+ /* check that the gid is valid for this domain */
+ if (OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
+ DEBUG(2, ("User [%s] filtered out! (id out of range)\n",
+ state->name));
+ ret = EINVAL;
+ goto fail;
+ }
+
+ user_attrs = sysdb_new_attrs(state);
+ if (user_attrs == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = sysdb_attrs_get_el(state->attrs, SYSDB_ORIG_DN, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("Original DN is not available for [%s].\n", state->name));
+ } else {
+ DEBUG(7, ("Adding original DN [%s] to attributes of [%s].\n",
+ el->values[0].data, state->name));
+ ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_DN,
+ (const char *) el->values[0].data);
+ if (ret) {
+ goto fail;
+ }
+ }
+
+ ret = sysdb_attrs_get_el(state->attrs, SYSDB_MEMBEROF, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("Original memberOf is not available for [%s].\n",
+ state->name));
+ } else {
+ DEBUG(7, ("Adding original memberOf attributes to [%s].\n",
+ state->name));
+ for (i = 0; i < el->num_values; i++) {
+ ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF,
+ (const char *) el->values[i].data);
+ if (ret) {
+ goto fail;
+ }
+ }
+ }
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("Original mod-Timestamp is not available for [%s].\n",
+ state->name));
+ } else {
+ ret = sysdb_attrs_add_string(user_attrs,
+ opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name,
+ (const char*)el->values[0].data);
+ if (ret) {
+ goto fail;
+ }
+ state->timestamp = talloc_strdup(state,
+ (const char*)el->values[0].data);
+ if (!state->timestamp) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ ret = sysdb_attrs_get_el(state->attrs,
+ opts->user_map[SDAP_AT_USER_PRINC].sys_name, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("User principle is not available for [%s].\n", state->name));
+ } else {
+ upn = talloc_strdup(user_attrs, (const char*) el->values[0].data);
+ if (!upn) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
+ make_realm_upper_case(upn);
+ }
+ DEBUG(7, ("Adding user principle [%s] to attributes of [%s].\n",
+ upn, state->name));
+ ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
+ if (ret) {
+ goto fail;
+ }
+ }
+
+ for (i = SDAP_FIRST_EXTRA_USER_AT; i < SDAP_OPTS_USER; i++) {
+ ret = sysdb_attrs_get_el(state->attrs, opts->user_map[i].sys_name, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values > 0) {
+ DEBUG(9, ("Adding [%s]=[%s] to user attributes.\n",
+ opts->user_map[i].sys_name,
+ (const char*) el->values[0].data));
+ val = talloc_strdup(user_attrs, (const char*) el->values[0].data);
+ if (val == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ ret = sysdb_attrs_add_string(user_attrs,
+ opts->user_map[i].sys_name, val);
+ if (ret) {
+ goto fail;
+ }
+ }
+ }
+
+ cache_timeout = dp_opt_get_int(opts->basic, SDAP_ENTRY_CACHE_TIMEOUT);
+
+ if (is_initgr) {
+ ret = sysdb_attrs_add_time_t(user_attrs, SYSDB_INITGR_EXPIRE,
+ (cache_timeout ?
+ (time(NULL) + cache_timeout) : 0));
+ if (ret) {
+ goto fail;
+ }
+ }
+
+ DEBUG(6, ("Storing info for user %s\n", state->name));
+
+ subreq = sysdb_store_user_send(state, state->ev, state->handle,
+ state->dom, state->name, pwd,
+ uid, gid, gecos, homedir, shell,
+ user_attrs, cache_timeout);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, sdap_save_user_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_save_user_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_save_user_state *state = tevent_req_data(req,
+ struct sdap_save_user_state);
+ int ret;
+
+ ret = sysdb_store_user_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("Failed to save user %s\n", state->name));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int sdap_save_user_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, char **timestamp)
+{
+ struct sdap_save_user_state *state = tevent_req_data(req,
+ struct sdap_save_user_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (timestamp) {
+ *timestamp = talloc_steal(mem_ctx, state->timestamp);
+ }
+
+ return EOK;
+}
+
+
+/* ==Generic-Function-to-save-multiple-users============================= */
+
+struct sdap_save_users_state {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sdap_options *opts;
+ struct sss_domain_info *dom;
+
+ struct sysdb_attrs **users;
+ int count;
+ int cur;
+
+ struct sysdb_handle *handle;
+
+ char *higher_timestamp;
+};
+
+static void sdap_save_users_trans(struct tevent_req *subreq);
+static void sdap_save_users_store(struct tevent_req *req);
+static void sdap_save_users_process(struct tevent_req *subreq);
+struct tevent_req *sdap_save_users_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sysdb_attrs **users,
+ int num_users)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_save_users_state *state;
+
+ req = tevent_req_create(memctx, &state, struct sdap_save_users_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sysdb = sysdb;
+ state->dom = dom;
+ state->users = users;
+ state->count = num_users;
+ state->cur = 0;
+ state->handle = NULL;
+ state->higher_timestamp = NULL;
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ tevent_req_post(req, ev);
+ return req;
+ }
+ tevent_req_set_callback(subreq, sdap_save_users_trans, req);
+
+ return req;
+}
+
+static void sdap_save_users_trans(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct sdap_save_users_state *state;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_save_users_state);
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sdap_save_users_store(req);
+}
+
+static void sdap_save_users_store(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct sdap_save_users_state *state;
+
+ state = tevent_req_data(req, struct sdap_save_users_state);
+
+ subreq = sdap_save_user_send(state, state->ev, state->handle,
+ state->opts, state->dom,
+ state->users[state->cur], false);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_save_users_process, req);
+}
+
+static void sdap_save_users_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct sdap_save_users_state *state;
+ char *timestamp = NULL;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_save_users_state);
+
+ ret = sdap_save_user_recv(subreq, state, &timestamp);
+ talloc_zfree(subreq);
+
+ /* Do not fail completely on errors.
+ * Just report the failure to save and go on */
+ if (ret) {
+ DEBUG(2, ("Failed to store user %d. Ignoring.\n", state->cur));
+ } else {
+ DEBUG(9, ("User %d processed!\n", state->cur));
+ }
+
+ if (timestamp) {
+ if (state->higher_timestamp) {
+ if (strcmp(timestamp, state->higher_timestamp) > 0) {
+ talloc_zfree(state->higher_timestamp);
+ state->higher_timestamp = timestamp;
+ } else {
+ talloc_zfree(timestamp);
+ }
+ } else {
+ state->higher_timestamp = timestamp;
+ }
+ }
+
+ state->cur++;
+ if (state->cur < state->count) {
+ sdap_save_users_store(req);
+ } else {
+ subreq = sysdb_transaction_commit_send(state, state->ev,
+ state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ /* sysdb_transaction_complete will call tevent_req_done(req) */
+ tevent_req_set_callback(subreq, sysdb_transaction_complete, req);
+ }
+}
+
+static int sdap_save_users_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, char **timestamp)
+{
+ struct sdap_save_users_state *state = tevent_req_data(req,
+ struct sdap_save_users_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (timestamp) {
+ *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
+ }
+
+ return EOK;
+}
+
+
+/* ==Search-Users-with-filter============================================= */
+
+struct sdap_get_users_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+ struct sdap_handle *sh;
+ struct sss_domain_info *dom;
+ struct sysdb_ctx *sysdb;
+ const char **attrs;
+ const char *filter;
+
+ char *higher_timestamp;
+ struct sysdb_attrs **users;
+ size_t count;
+};
+
+static void sdap_get_users_process(struct tevent_req *subreq);
+static void sdap_get_users_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ const char **attrs,
+ const char *filter)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_get_users_state *state;
+
+ req = tevent_req_create(memctx, &state, struct sdap_get_users_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->dom = dom;
+ state->sh = sh;
+ state->sysdb = sysdb;
+ state->filter = filter;
+ state->attrs = attrs;
+ state->higher_timestamp = NULL;
+ state->users = NULL;
+ state->count = 0;
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ dp_opt_get_string(state->opts->basic,
+ SDAP_USER_SEARCH_BASE),
+ LDAP_SCOPE_SUBTREE,
+ state->filter, state->attrs,
+ state->opts->user_map, SDAP_OPTS_USER);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_get_users_process, req);
+
+ return req;
+}
+
+static void sdap_get_users_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_users_state *state = tevent_req_data(req,
+ struct sdap_get_users_state);
+ int ret;
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->count, &state->users);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(6, ("Search for users, returned %d results.\n", state->count));
+
+ if (state->count == 0) {
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+
+ subreq = sdap_save_users_send(state, state->ev, state->dom,
+ state->sysdb, state->opts,
+ state->users, state->count);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_users_done, req);
+}
+
+static void sdap_get_users_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_users_state *state = tevent_req_data(req,
+ struct sdap_get_users_state);
+ int ret;
+
+ DEBUG(9, ("Saving %d Users - Done\n", state->count));
+
+ ret = sdap_save_users_recv(subreq, state, &state->higher_timestamp);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("Failed to store users.\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int sdap_get_users_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, char **timestamp)
+{
+ struct sdap_get_users_state *state = tevent_req_data(req,
+ struct sdap_get_users_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (timestamp) {
+ *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
+ }
+
+ return EOK;
+}
+
+/* ==Group-Parsing Routines=============================================== */
+
+struct sdap_orig_entry_state {
+ int done;
+};
+
+static void sdap_find_entry_by_origDN_done(struct tevent_req *req)
+{
+ struct sdap_orig_entry_state *state = tevent_req_callback_data(req,
+ struct sdap_orig_entry_state);
+ state->done = 1;
+}
+
+/* WARNING: this is a sync routine for now */
+static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct sss_domain_info *domain,
+ const char *orig_dn,
+ char **localdn)
+{
+ struct tevent_req *req;
+ struct sdap_orig_entry_state *state;
+ static const char *attrs[] = { NULL };
+ struct ldb_dn *base_dn;
+ char *filter;
+ struct ldb_message **msgs;
+ size_t num_msgs;
+ int ret;
+
+ state = talloc_zero(memctx, struct sdap_orig_entry_state);
+ if (!state) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = talloc_asprintf(state, "%s=%s", SYSDB_ORIG_DN, orig_dn);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ base_dn = sysdb_domain_dn(sysdb_handle_get_ctx(handle),
+ state, domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ req = sysdb_search_entry_send(state, ev, handle, base_dn,
+ LDB_SCOPE_SUBTREE, filter, attrs);
+ if (!req) {
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(req, sdap_find_entry_by_origDN_done, state);
+
+ /* WARNING: SYNC LOOP HERE */
+ tevent_loop_allow_nesting(ev);
+ while (state->done == 0) {
+ tevent_loop_once(ev);
+ }
+
+ ret = sysdb_search_entry_recv(req, state, &num_msgs, &msgs);
+ if (ret) {
+ goto done;
+ }
+ if (num_msgs != 1) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *localdn = talloc_strdup(memctx, ldb_dn_get_linearized(msgs[0]->dn));
+ if (!*localdn) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_zfree(state);
+ return ret;
+}
+
+static int sdap_fill_memberships(struct sysdb_attrs *group_attrs,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct sdap_options *opts,
+ struct sss_domain_info *domain,
+ struct ldb_val *values,
+ int num_values)
+{
+ struct ldb_message_element *el;
+ int i, j;
+ int ret;
+
+ switch (opts->schema_type) {
+ case SDAP_SCHEMA_RFC2307:
+ DEBUG(9, ("[RFC2307 Schema]\n"));
+
+ ret = sysdb_attrs_users_from_ldb_vals(group_attrs, SYSDB_MEMBER,
+ domain->name,
+ values, num_values);
+ if (ret) {
+ goto done;
+ }
+
+ break;
+
+ case SDAP_SCHEMA_RFC2307BIS:
+ case SDAP_SCHEMA_IPA_V1:
+ case SDAP_SCHEMA_AD:
+ DEBUG(9, ("[IPA or AD Schema]\n"));
+
+ ret = sysdb_attrs_get_el(group_attrs, SYSDB_MEMBER, &el);
+ if (ret) {
+ goto done;
+ }
+
+ /* Just allocate both big enough to contain all members for now */
+ el->values = talloc_realloc(el, el->values, struct ldb_val,
+ el->num_values + num_values);
+ if (!el->values) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0, j = el->num_values; i < num_values; i++) {
+
+ /* sync search entry with this as origDN */
+ ret = sdap_find_entry_by_origDN(el->values, ev,
+ handle, domain,
+ (char *)values[i].data,
+ (char **)&el->values[j].data);
+ if (ret != EOK) {
+ if (ret != ENOENT) {
+ goto done;
+ }
+
+ DEBUG(7, (" member #%d (%s): not found!\n",
+ i, (char *)values[i].data));
+ } else {
+ DEBUG(7, (" member #%d (%s): [%s]\n",
+ i, (char *)values[i].data,
+ (char *)el->values[j].data));
+
+ el->values[j].length = strlen((char *)el->values[j].data);
+ j++;
+ }
+ }
+ el->num_values = j;
+
+ break;
+
+ default:
+ DEBUG(0, ("FATAL ERROR: Unhandled schema type! (%d)\n",
+ opts->schema_type));
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+/* ==Save-Group-Entry===================================================== */
+
+struct sdap_save_group_state {
+ struct tevent_context *ev;
+ struct sysdb_handle *handle;
+ struct sdap_options *opts;
+
+ struct sss_domain_info *dom;
+
+ const char *name;
+ char *timestamp;
+};
+
+static void sdap_save_group_done(struct tevent_req *subreq);
+
+ /* FIXME: support non legacy */
+ /* FIXME: support storing additional attributes */
+
+static struct tevent_req *sdap_save_group_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct sdap_options *opts,
+ struct sss_domain_info *dom,
+ struct sysdb_attrs *attrs,
+ bool store_members)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_save_group_state *state;
+ struct ldb_message_element *el;
+ struct sysdb_attrs *group_attrs;
+ long int l;
+ gid_t gid;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct sdap_save_group_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->handle = handle;
+ state->dom = dom;
+ state->opts = opts;
+ state->timestamp = NULL;
+
+ ret = sysdb_attrs_get_el(attrs,
+ opts->group_map[SDAP_AT_GROUP_NAME].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) {
+ ret = EINVAL;
+ goto fail;
+ }
+ state->name = (const char *)el->values[0].data;
+
+ ret = sysdb_attrs_get_el(attrs,
+ opts->group_map[SDAP_AT_GROUP_GID].sys_name, &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) {
+ DEBUG(1, ("no gid provided for [%s] in domain [%s].\n",
+ state->name, dom->name));
+ ret = EINVAL;
+ goto fail;
+ }
+ errno = 0;
+ l = strtol((const char *)el->values[0].data, NULL, 0);
+ if (errno) {
+ ret = EINVAL;
+ goto fail;
+ }
+ gid = l;
+
+ /* check that the gid is valid for this domain */
+ if (OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
+ DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
+ state->name));
+ ret = EINVAL;
+ goto fail;
+ }
+
+ group_attrs = sysdb_new_attrs(state);
+ if (!group_attrs) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("Original DN is not available for [%s].\n", state->name));
+ } else {
+ DEBUG(7, ("Adding original DN [%s] to attributes of [%s].\n",
+ el->values[0].data, state->name));
+ ret = sysdb_attrs_add_string(group_attrs, SYSDB_ORIG_DN,
+ (const char *)el->values[0].data);
+ if (ret) {
+ goto fail;
+ }
+ }
+
+ ret = sysdb_attrs_get_el(attrs,
+ opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("Original mod-Timestamp is not available for [%s].\n",
+ state->name));
+ } else {
+ ret = sysdb_attrs_add_string(group_attrs,
+ opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name,
+ (const char*)el->values[0].data);
+ if (ret) {
+ goto fail;
+ }
+ state->timestamp = talloc_strdup(state,
+ (const char*)el->values[0].data);
+ if (!state->timestamp) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (store_members) {
+ ret = sysdb_attrs_get_el(attrs,
+ opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
+ if (ret != EOK) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("No members for group [%s]\n", state->name));
+
+ } else {
+ DEBUG(7, ("Adding member users to group [%s]\n", state->name));
+
+ ret = sdap_fill_memberships(group_attrs, ev, handle, opts, dom,
+ el->values, el->num_values);
+ if (ret) {
+ goto fail;
+ }
+ }
+ }
+
+ DEBUG(6, ("Storing info for group %s\n", state->name));
+
+ subreq = sysdb_store_group_send(state, state->ev,
+ state->handle, state->dom,
+ state->name, gid,
+ group_attrs,
+ dp_opt_get_int(opts->basic,
+ SDAP_ENTRY_CACHE_TIMEOUT));
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, sdap_save_group_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_save_group_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_save_group_state *state = tevent_req_data(req,
+ struct sdap_save_group_state);
+ int ret;
+
+ ret = sysdb_store_group_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("Failed to save group %s [%d]\n", state->name, ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int sdap_save_group_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, char **timestamp)
+{
+ struct sdap_save_group_state *state = tevent_req_data(req,
+ struct sdap_save_group_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if ( timestamp ) {
+ *timestamp = talloc_steal(mem_ctx, state->timestamp);
+ }
+
+ return EOK;
+}
+
+
+/* ==Save-Group-Memebrs=================================================== */
+
+struct sdap_save_grpmem_state {
+ struct tevent_context *ev;
+ struct sysdb_handle *handle;
+ struct sdap_options *opts;
+
+ struct sss_domain_info *dom;
+
+ const char *name;
+};
+
+static void sdap_save_grpmem_done(struct tevent_req *subreq);
+
+ /* FIXME: support non legacy */
+ /* FIXME: support storing additional attributes */
+
+static struct tevent_req *sdap_save_grpmem_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct sdap_options *opts,
+ struct sss_domain_info *dom,
+ struct sysdb_attrs *attrs)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_save_grpmem_state *state;
+ struct ldb_message_element *el;
+ struct sysdb_attrs *group_attrs = NULL;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct sdap_save_grpmem_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->handle = handle;
+ state->dom = dom;
+ state->opts = opts;
+
+ ret = sysdb_attrs_get_string(attrs,
+ opts->group_map[SDAP_AT_GROUP_NAME].sys_name,
+ &state->name);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = sysdb_attrs_get_el(attrs,
+ opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
+ if (ret != EOK) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("No members for group [%s]\n", state->name));
+
+ } else {
+ DEBUG(7, ("Adding member users to group [%s]\n", state->name));
+
+ group_attrs = sysdb_new_attrs(state);
+ if (!group_attrs) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = sdap_fill_memberships(group_attrs, ev, handle, opts, dom,
+ el->values, el->num_values);
+ if (ret) {
+ goto fail;
+ }
+ }
+
+ DEBUG(6, ("Storing members for group %s\n", state->name));
+
+ subreq = sysdb_store_group_send(state, state->ev,
+ state->handle, state->dom,
+ state->name, 0,
+ group_attrs,
+ dp_opt_get_int(opts->basic,
+ SDAP_ENTRY_CACHE_TIMEOUT));
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, sdap_save_grpmem_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_save_grpmem_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_save_grpmem_state *state = tevent_req_data(req,
+ struct sdap_save_grpmem_state);
+ int ret;
+
+ ret = sysdb_store_group_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("Failed to save group members for %s [%d]\n",
+ state->name, ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int sdap_save_grpmem_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+/* ==Generic-Function-to-save-multiple-groups============================= */
+
+struct sdap_save_groups_state {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sdap_options *opts;
+ struct sss_domain_info *dom;
+
+ struct sysdb_attrs **groups;
+ int count;
+ int cur;
+ bool twopass;
+
+ struct sysdb_handle *handle;
+
+ char *higher_timestamp;
+};
+
+static void sdap_save_groups_trans(struct tevent_req *subreq);
+static void sdap_save_groups_save(struct tevent_req *req);
+static void sdap_save_groups_loop(struct tevent_req *subreq);
+static void sdap_save_groups_mem_save(struct tevent_req *req);
+static void sdap_save_groups_mem_loop(struct tevent_req *subreq);
+struct tevent_req *sdap_save_groups_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sysdb_attrs **groups,
+ int num_groups)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_save_groups_state *state;
+
+ req = tevent_req_create(memctx, &state, struct sdap_save_groups_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sysdb = sysdb;
+ state->dom = dom;
+ state->groups = groups;
+ state->count = num_groups;
+ state->cur = 0;
+ state->handle = NULL;
+ state->higher_timestamp = NULL;
+
+ switch (opts->schema_type) {
+ case SDAP_SCHEMA_RFC2307:
+ state->twopass = false;
+ break;
+
+ case SDAP_SCHEMA_RFC2307BIS:
+ case SDAP_SCHEMA_IPA_V1:
+ case SDAP_SCHEMA_AD:
+ state->twopass = true;
+ break;
+
+ default:
+ tevent_req_error(req, EINVAL);
+ tevent_req_post(req, ev);
+ return req;
+ }
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ tevent_req_post(req, ev);
+ return req;
+ }
+ tevent_req_set_callback(subreq, sdap_save_groups_trans, req);
+
+ return req;
+}
+
+static void sdap_save_groups_trans(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct sdap_save_groups_state *state;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_save_groups_state);
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sdap_save_groups_save(req);
+}
+
+static void sdap_save_groups_save(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct sdap_save_groups_state *state;
+
+ state = tevent_req_data(req, struct sdap_save_groups_state);
+
+ /* if 2 pass savemembers = false */
+ subreq = sdap_save_group_send(state, state->ev, state->handle,
+ state->opts, state->dom,
+ state->groups[state->cur],
+ (!state->twopass));
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_save_groups_loop, req);
+}
+
+static void sdap_save_groups_loop(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct sdap_save_groups_state *state;
+ char *timestamp = NULL;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_save_groups_state);
+
+ ret = sdap_save_group_recv(subreq, state, &timestamp);
+ talloc_zfree(subreq);
+
+ /* Do not fail completely on errors.
+ * Just report the failure to save and go on */
+ if (ret) {
+ DEBUG(2, ("Failed to store group %d. Ignoring.\n", state->cur));
+ } else {
+ DEBUG(9, ("Group %d processed!\n", state->cur));
+ }
+
+ if (timestamp) {
+ if (state->higher_timestamp) {
+ if (strcmp(timestamp, state->higher_timestamp) > 0) {
+ talloc_zfree(state->higher_timestamp);
+ state->higher_timestamp = timestamp;
+ } else {
+ talloc_zfree(timestamp);
+ }
+ } else {
+ state->higher_timestamp = timestamp;
+ }
+ }
+
+ state->cur++;
+ if (state->cur < state->count) {
+
+ sdap_save_groups_save(req);
+
+ } else if (state->twopass) {
+
+ state->cur = 0;
+ sdap_save_groups_mem_save(req);
+
+ } else {
+
+ subreq = sysdb_transaction_commit_send(state, state->ev,
+ state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ /* sysdb_transaction_complete will call tevent_req_done(req) */
+ tevent_req_set_callback(subreq, sysdb_transaction_complete, req);
+ }
+}
+
+static void sdap_save_groups_mem_save(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct sdap_save_groups_state *state;
+
+ state = tevent_req_data(req, struct sdap_save_groups_state);
+
+ subreq = sdap_save_grpmem_send(state, state->ev, state->handle,
+ state->opts, state->dom,
+ state->groups[state->cur]);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_save_groups_mem_loop, req);
+}
+
+static void sdap_save_groups_mem_loop(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct sdap_save_groups_state *state;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_save_groups_state);
+
+ ret = sdap_save_grpmem_recv(subreq);
+ talloc_zfree(subreq);
+
+ /* Do not fail completely on errors.
+ * Just report the failure to save and go on */
+ if (ret) {
+ DEBUG(2, ("Failed to store group %d. Ignoring.\n", state->cur));
+ }
+
+ state->cur++;
+ if (state->cur < state->count) {
+
+ sdap_save_groups_mem_save(req);
+
+ } else {
+
+ subreq = sysdb_transaction_commit_send(state, state->ev,
+ state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ /* sysdb_transaction_complete will call tevent_req_done(req) */
+ tevent_req_set_callback(subreq, sysdb_transaction_complete, req);
+ }
+}
+
+static int sdap_save_groups_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, char **timestamp)
+{
+ struct sdap_save_groups_state *state = tevent_req_data(req,
+ struct sdap_save_groups_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (timestamp) {
+ *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
+ }
+
+ return EOK;
+}
+
+
+/* ==Search-Groups-with-filter============================================ */
+
+struct sdap_get_groups_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+ struct sdap_handle *sh;
+ struct sss_domain_info *dom;
+ struct sysdb_ctx *sysdb;
+ const char **attrs;
+ const char *filter;
+
+ char *higher_timestamp;
+ struct sysdb_attrs **groups;
+ size_t count;
+};
+
+static void sdap_get_groups_process(struct tevent_req *subreq);
+static void sdap_get_groups_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ const char **attrs,
+ const char *filter)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_get_groups_state *state;
+
+ req = tevent_req_create(memctx, &state, struct sdap_get_groups_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->dom = dom;
+ state->sh = sh;
+ state->sysdb = sysdb;
+ state->filter = filter;
+ state->attrs = attrs;
+ state->higher_timestamp = NULL;
+ state->groups = NULL;
+ state->count = 0;
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ dp_opt_get_string(state->opts->basic,
+ SDAP_GROUP_SEARCH_BASE),
+ LDAP_SCOPE_SUBTREE,
+ state->filter, state->attrs,
+ state->opts->group_map, SDAP_OPTS_GROUP);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_get_groups_process, req);
+
+ return req;
+}
+
+static void sdap_get_groups_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_groups_state *state = tevent_req_data(req,
+ struct sdap_get_groups_state);
+ int ret;
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->count, &state->groups);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(6, ("Search for groups, returned %d results.\n", state->count));
+
+ if (state->count == 0) {
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+
+ subreq = sdap_save_groups_send(state, state->ev, state->dom,
+ state->sysdb, state->opts,
+ state->groups, state->count);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_groups_done, req);
+}
+
+static void sdap_get_groups_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_groups_state *state = tevent_req_data(req,
+ struct sdap_get_groups_state);
+ int ret;
+
+ DEBUG(9, ("Saving %d Groups - Done\n", state->count));
+
+ ret = sdap_save_groups_recv(subreq, state, &state->higher_timestamp);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(2, ("Failed to store groups.\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int sdap_get_groups_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, char **timestamp)
+{
+ struct sdap_get_groups_state *state = tevent_req_data(req,
+ struct sdap_get_groups_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (timestamp) {
+ *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
+ }
+
+ return EOK;
+}
+
+
+/* ==Initgr-call-(groups-a-user-is-member-of)-RFC2307-Classic/BIS========= */
+
+struct sdap_initgr_rfc2307_state {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sdap_options *opts;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+
+ struct sdap_op *op;
+};
+
+static void sdap_initgr_rfc2307_process(struct tevent_req *subreq);
+static void sdap_initgr_rfc2307_done(struct tevent_req *subreq);
+struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ const char *base_dn,
+ const char *name,
+ const char **grp_attrs)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_initgr_rfc2307_state *state;
+ const char *filter;
+
+ req = tevent_req_create(memctx, &state, struct sdap_initgr_rfc2307_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sysdb = sysdb;
+ state->dom = dom;
+ state->sh = sh;
+ state->op = NULL;
+
+ filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
+ opts->group_map[SDAP_AT_GROUP_MEMBER].name,
+ name, opts->group_map[SDAP_OC_GROUP].name);
+ if (!filter) {
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts,
+ state->sh, base_dn, LDAP_SCOPE_SUBTREE,
+ filter, grp_attrs,
+ state->opts->group_map, SDAP_OPTS_GROUP);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_initgr_rfc2307_process, req);
+
+ return req;
+}
+
+static void sdap_initgr_rfc2307_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct sdap_initgr_rfc2307_state *state;
+ struct sysdb_attrs **groups;
+ size_t count;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_initgr_rfc2307_state);
+
+ ret = sdap_get_generic_recv(subreq, state, &count, &groups);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (count == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = sdap_save_groups_send(state, state->ev, state->dom,
+ state->sysdb, state->opts,
+ groups, count);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_initgr_rfc2307_done, req);
+}
+
+static void sdap_initgr_rfc2307_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = sdap_save_groups_recv(subreq, NULL, NULL);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int sdap_initgr_rfc2307_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+/* ==Initgr-call-(groups-a-user-is-member-of)-nested-groups=============== */
+
+struct sdap_initgr_nested_state {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sdap_options *opts;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+
+ const char **grp_attrs;
+
+ char *filter;
+ char **group_dns;
+ int count;
+ int cur;
+
+ struct sdap_op *op;
+
+ struct sysdb_attrs **groups;
+ int groups_cur;
+};
+
+static void sdap_initgr_nested_search(struct tevent_req *subreq);
+static void sdap_initgr_nested_store(struct tevent_req *req);
+static void sdap_initgr_nested_done(struct tevent_req *subreq);
+static struct tevent_req *sdap_initgr_nested_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ struct sysdb_attrs *user,
+ const char **grp_attrs)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_initgr_nested_state *state;
+ struct ldb_message_element *el;
+ int i, ret;
+
+ req = tevent_req_create(memctx, &state, struct sdap_initgr_nested_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sysdb = sysdb;
+ state->dom = dom;
+ state->sh = sh;
+ state->grp_attrs = grp_attrs;
+ state->op = NULL;
+
+ state->filter = talloc_asprintf(state, "(objectclass=%s)",
+ opts->group_map[SDAP_OC_GROUP].name);
+ if (!state->filter) {
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ /* TODO: test rootDSE for deref support and use it if available */
+ /* TODO: or test rootDSE for ASQ support and use it if available */
+
+ ret = sysdb_attrs_get_el(user, SYSDB_MEMBEROF, &el);
+ if (ret || !el || el->num_values == 0) {
+ DEBUG(4, ("User entry lacks original memberof ?\n"));
+ /* user with no groups ? */
+ tevent_req_error(req, ENOENT);
+ tevent_req_post(req, ev);
+ }
+ state->count = el->num_values;
+
+ state->groups = talloc_zero_array(state, struct sysdb_attrs *,
+ state->count + 1);;
+ if (!state->groups) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ state->groups_cur = 0;
+
+ state->group_dns = talloc_array(state, char *, state->count + 1);
+ if (!state->group_dns) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ for (i = 0; i < state->count; i++) {
+ state->group_dns[i] = talloc_strdup(state->group_dns,
+ (char *)el->values[i].data);
+ if (!state->group_dns[i]) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ }
+ state->group_dns[i] = NULL; /* terminate */
+ state->cur = 0;
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ state->group_dns[state->cur],
+ LDAP_SCOPE_BASE,
+ state->filter, state->grp_attrs,
+ state->opts->group_map, SDAP_OPTS_GROUP);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_initgr_nested_search, req);
+
+ return req;
+}
+
+static void sdap_initgr_nested_search(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct sdap_initgr_nested_state *state;
+ struct sysdb_attrs **groups;
+ size_t count;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_initgr_nested_state);
+
+ ret = sdap_get_generic_recv(subreq, state, &count, &groups);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (count == 1) {
+ state->groups[state->groups_cur] = groups[0];
+ state->groups_cur++;
+ } else {
+ DEBUG(2, ("Search for group %s, returned %d results. Skipping\n",
+ state->group_dns[state->cur], count));
+ }
+
+ state->cur++;
+ if (state->cur < state->count) {
+ subreq = sdap_get_generic_send(state, state->ev,
+ state->opts, state->sh,
+ state->group_dns[state->cur],
+ LDAP_SCOPE_BASE,
+ state->filter, state->grp_attrs,
+ state->opts->group_map,
+ SDAP_OPTS_GROUP);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_initgr_nested_search, req);
+ } else {
+ sdap_initgr_nested_store(req);
+ }
+}
+
+static void sdap_initgr_nested_store(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct sdap_initgr_nested_state *state;
+
+ state = tevent_req_data(req, struct sdap_initgr_nested_state);
+
+ subreq = sdap_save_groups_send(state, state->ev, state->dom,
+ state->sysdb, state->opts,
+ state->groups, state->groups_cur);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_initgr_nested_done, req);
+}
+
+static void sdap_initgr_nested_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = sdap_save_groups_recv(subreq, NULL, NULL);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int sdap_initgr_nested_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
+/* ==Initgr-call-(groups-a-user-is-member-of)============================= */
+
+struct sdap_get_initgr_state {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sdap_options *opts;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+ const char *name;
+ const char **grp_attrs;
+
+ struct sysdb_attrs *orig_user;
+
+ struct sysdb_handle *handle;
+};
+
+static void sdap_get_initgr_user(struct tevent_req *subreq);
+static void sdap_get_initgr_store(struct tevent_req *subreq);
+static void sdap_get_initgr_commit(struct tevent_req *subreq);
+static void sdap_get_initgr_process(struct tevent_req *subreq);
+static void sdap_get_initgr_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ const char *name,
+ const char **grp_attrs)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_get_initgr_state *state;
+ const char *base_dn;
+ char *filter;
+ const char **attrs;
+ int ret;
+
+ DEBUG(9, ("Retrieving info for initgroups call\n"));
+
+ req = tevent_req_create(memctx, &state, struct sdap_get_initgr_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sysdb = sysdb;
+ state->dom = dom;
+ state->sh = sh;
+ state->name = name;
+ state->grp_attrs = grp_attrs;
+ state->orig_user = NULL;
+
+ filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
+ state->opts->user_map[SDAP_AT_USER_NAME].name,
+ state->name,
+ state->opts->user_map[SDAP_OC_USER].name);
+ if (!filter) {
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ base_dn = dp_opt_get_string(state->opts->basic,
+ SDAP_USER_SEARCH_BASE);
+ if (!base_dn) {
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ ret = build_attrs_from_map(state, state->opts->user_map,
+ SDAP_OPTS_USER, &attrs);
+ if (ret) {
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ subreq = sdap_get_generic_send(state, state->ev,
+ state->opts, state->sh,
+ base_dn, LDAP_SCOPE_SUBTREE,
+ filter, attrs,
+ state->opts->user_map, SDAP_OPTS_USER);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_get_initgr_user, req);
+
+ return req;
+}
+
+static void sdap_get_initgr_user(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_initgr_state *state = tevent_req_data(req,
+ struct sdap_get_initgr_state);
+ struct sysdb_attrs **usr_attrs;
+ size_t count;
+ int ret;
+
+ DEBUG(9, ("Receiving info for the user\n"));
+
+ ret = sdap_get_generic_recv(subreq, state, &count, &usr_attrs);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (count != 1) {
+ DEBUG(2, ("Expected one user entry and got %d\n", count));
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+
+ state->orig_user = usr_attrs[0];
+
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_initgr_store, req);
+}
+
+static void sdap_get_initgr_store(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_initgr_state *state = tevent_req_data(req,
+ struct sdap_get_initgr_state);
+ int ret;
+
+ DEBUG(9, ("Storing the user\n"));
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_save_user_send(state, state->ev, state->handle,
+ state->opts, state->dom,
+ state->orig_user, true);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_initgr_commit, req);
+}
+
+static void sdap_get_initgr_commit(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_initgr_state *state = tevent_req_data(req,
+ struct sdap_get_initgr_state);
+ int ret;
+
+ DEBUG(9, ("Commit change\n"));
+
+ ret = sdap_save_user_recv(subreq, NULL, NULL);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_initgr_process, req);
+}
+
+static void sdap_get_initgr_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_initgr_state *state = tevent_req_data(req,
+ struct sdap_get_initgr_state);
+ int ret;
+
+ DEBUG(9, ("Process user's groups\n"));
+
+ ret = sysdb_transaction_commit_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ switch (state->opts->schema_type) {
+ case SDAP_SCHEMA_RFC2307:
+ subreq = sdap_initgr_rfc2307_send(state, state->ev, state->opts,
+ state->sysdb, state->dom, state->sh,
+ dp_opt_get_string(state->opts->basic,
+ SDAP_GROUP_SEARCH_BASE),
+ state->name, state->grp_attrs);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_initgr_done, req);
+ break;
+
+ case SDAP_SCHEMA_RFC2307BIS:
+ case SDAP_SCHEMA_IPA_V1:
+ case SDAP_SCHEMA_AD:
+ /* TODO: AD uses a different member/memberof schema
+ * We need an AD specific call that is able to unroll
+ * nested groups by doing extensive recursive searches */
+
+ subreq = sdap_initgr_nested_send(state, state->ev, state->opts,
+ state->sysdb, state->dom, state->sh,
+ state->orig_user, state->grp_attrs);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_initgr_done, req);
+ return;
+
+ default:
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+}
+
+static void sdap_get_initgr_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_initgr_state *state = tevent_req_data(req,
+ struct sdap_get_initgr_state);
+ int ret;
+
+ DEBUG(9, ("Initgroups done\n"));
+
+ switch (state->opts->schema_type) {
+ case SDAP_SCHEMA_RFC2307:
+
+ ret = sdap_initgr_rfc2307_recv(subreq);
+ break;
+
+ case SDAP_SCHEMA_RFC2307BIS:
+ case SDAP_SCHEMA_IPA_V1:
+ case SDAP_SCHEMA_AD:
+
+ ret = sdap_initgr_nested_recv(subreq);
+ break;
+
+ default:
+
+ ret = EINVAL;
+ break;
+ }
+
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int sdap_get_initgr_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
new file mode 100644
index 000000000..18e47d3b7
--- /dev/null
+++ b/src/providers/ldap/sdap_async_connection.c
@@ -0,0 +1,1141 @@
+/*
+ SSSD
+
+ Async LDAP Helper routines
+
+ Copyright (C) Simo Sorce <ssorce@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 <sasl/sasl.h>
+#include "util/util.h"
+#include "util/sss_krb5.h"
+#include "providers/ldap/sdap_async_private.h"
+
+#define LDAP_X_SSSD_PASSWORD_EXPIRED 0x555D
+
+/* ==Connect-to-LDAP-Server=============================================== */
+
+struct sdap_connect_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+ struct sdap_handle *sh;
+
+ struct sdap_op *op;
+
+ struct sdap_msg *reply;
+ int result;
+};
+
+static void sdap_connect_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt);
+
+struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ const char *uri,
+ bool use_start_tls)
+{
+ struct tevent_req *req;
+ struct sdap_connect_state *state;
+ struct timeval tv;
+ int ver;
+ int lret;
+ int ret = EOK;
+ int msgid;
+ bool ldap_referrals;
+
+ req = tevent_req_create(memctx, &state, struct sdap_connect_state);
+ if (!req) return NULL;
+
+ state->reply = talloc(state, struct sdap_msg);
+ if (!state->reply) {
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sh = sdap_handle_create(state);
+ if (!state->sh) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ /* Initialize LDAP handler */
+ lret = ldap_initialize(&state->sh->ldap, uri);
+ if (lret != LDAP_SUCCESS) {
+ DEBUG(1, ("ldap_initialize failed: %s\n", ldap_err2string(lret)));
+ goto fail;
+ }
+
+ /* Force ldap version to 3 */
+ ver = LDAP_VERSION3;
+ lret = ldap_set_option(state->sh->ldap, LDAP_OPT_PROTOCOL_VERSION, &ver);
+ if (lret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("Failed to set ldap version to 3\n"));
+ goto fail;
+ }
+
+ /* Set Network Timeout */
+ tv.tv_sec = dp_opt_get_int(opts->basic, SDAP_NETWORK_TIMEOUT);
+ tv.tv_usec = 0;
+ lret = ldap_set_option(state->sh->ldap, LDAP_OPT_NETWORK_TIMEOUT, &tv);
+ if (lret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("Failed to set network timeout to %d\n",
+ dp_opt_get_int(opts->basic, SDAP_NETWORK_TIMEOUT)));
+ goto fail;
+ }
+
+ /* Set Default Timeout */
+ tv.tv_sec = dp_opt_get_int(opts->basic, SDAP_OPT_TIMEOUT);
+ tv.tv_usec = 0;
+ lret = ldap_set_option(state->sh->ldap, LDAP_OPT_TIMEOUT, &tv);
+ if (lret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("Failed to set default timeout to %d\n",
+ dp_opt_get_int(opts->basic, SDAP_OPT_TIMEOUT)));
+ goto fail;
+ }
+
+ /* Set Referral chasing */
+ ldap_referrals = dp_opt_get_bool(opts->basic, SDAP_REFERRALS);
+ lret = ldap_set_option(state->sh->ldap, LDAP_OPT_REFERRALS,
+ (ldap_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF));
+ if (lret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("Failed to set referral chasing to %s\n",
+ (ldap_referrals ? "LDAP_OPT_ON" : "LDAP_OPT_OFF")));
+ goto fail;
+ }
+
+#ifdef HAVE_LDAP_CONNCB
+ struct ldap_cb_data *cb_data;
+
+ /* add connection callback */
+ state->sh->conncb = talloc_zero(state->sh, struct ldap_conncb);
+ if (state->sh->conncb == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ cb_data = talloc_zero(state->sh->conncb, struct ldap_cb_data);
+ if (cb_data == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+ cb_data->sh = state->sh;
+ cb_data->ev = state->ev;
+
+ state->sh->conncb->lc_add = sdap_ldap_connect_callback_add;
+ state->sh->conncb->lc_del = sdap_ldap_connect_callback_del;
+ state->sh->conncb->lc_arg = cb_data;
+
+ lret = ldap_set_option(state->sh->ldap, LDAP_OPT_CONNECT_CB,
+ state->sh->conncb);
+ if (lret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("Failed to set connection callback\n"));
+ goto fail;
+ }
+#endif
+
+ /* if we do not use start_tls the connection is not really connected yet
+ * just fake an async procedure and leave connection to the bind call */
+ if (!use_start_tls) {
+ tevent_req_post(req, ev);
+ return req;
+ }
+
+ DEBUG(4, ("Executing START TLS\n"));
+
+ lret = ldap_start_tls(state->sh->ldap, NULL, NULL, &msgid);
+ if (lret != LDAP_SUCCESS) {
+ DEBUG(3, ("ldap_start_tls failed: [%s]", ldap_err2string(ret)));
+ goto fail;
+ }
+
+ state->sh->connected = true;
+#ifndef HAVE_LDAP_CONNCB
+ ret = sdap_install_ldap_callbacks(state->sh, state->ev);
+ if (ret) goto fail;
+#endif
+
+ /* FIXME: get timeouts from configuration, for now 5 secs. */
+ ret = sdap_op_add(state, ev, state->sh, msgid,
+ sdap_connect_done, req, 5, &state->op);
+ if (ret) {
+ DEBUG(1, ("Failed to set up operation!\n"));
+ goto fail;
+ }
+
+ return req;
+
+fail:
+ if (ret) {
+ tevent_req_error(req, ret);
+ } else {
+ if (lret == LDAP_SERVER_DOWN) {
+ tevent_req_error(req, ETIMEDOUT);
+ } else {
+ tevent_req_error(req, EIO);
+ }
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_connect_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct sdap_connect_state *state = tevent_req_data(req,
+ struct sdap_connect_state);
+ char *errmsg;
+ int ret;
+
+ if (error) {
+ tevent_req_error(req, error);
+ return;
+ }
+
+ state->reply = talloc_steal(state, reply);
+
+ ret = ldap_parse_result(state->sh->ldap, state->reply->msg,
+ &state->result, NULL, &errmsg, NULL, NULL, 0);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ DEBUG(3, ("START TLS result: %s(%d), %s\n",
+ ldap_err2string(state->result), state->result, errmsg));
+
+ if (ldap_tls_inplace(state->sh->ldap)) {
+ DEBUG(9, ("SSL/TLS handler already in place.\n"));
+ tevent_req_done(req);
+ return;
+ }
+
+/* FIXME: take care that ldap_install_tls might block */
+ ret = ldap_install_tls(state->sh->ldap);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(1, ("ldap_install_tls failed: [%d][%s]\n", ret,
+ ldap_err2string(ret)));
+ state->result = ret;
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int sdap_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *memctx,
+ struct sdap_handle **sh)
+{
+ struct sdap_connect_state *state = tevent_req_data(req,
+ struct sdap_connect_state);
+ enum tevent_req_state tstate;
+ uint64_t err = EIO;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ /* if tstate shows in progress, it is because
+ * we did not ask to perform tls, just pretend all is fine */
+ if (tstate != TEVENT_REQ_IN_PROGRESS) {
+ return err;
+ }
+ }
+
+ *sh = talloc_steal(memctx, state->sh);
+ if (!*sh) {
+ return ENOMEM;
+ }
+ return EOK;
+}
+
+/* ==Simple-Bind========================================================== */
+
+struct simple_bind_state {
+ struct tevent_context *ev;
+ struct sdap_handle *sh;
+ const char *user_dn;
+ struct berval *pw;
+
+ struct sdap_op *op;
+
+ struct sdap_msg *reply;
+ int result;
+};
+
+static void simple_bind_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt);
+
+static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ const char *user_dn,
+ struct berval *pw)
+{
+ struct tevent_req *req;
+ struct simple_bind_state *state;
+ int ret = EOK;
+ int msgid;
+ int ldap_err;
+ LDAPControl *request_controls[2];
+
+ req = tevent_req_create(memctx, &state, struct simple_bind_state);
+ if (!req) return NULL;
+
+ state->reply = talloc(state, struct sdap_msg);
+ if (!state->reply) {
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->sh = sh;
+ state->user_dn = user_dn;
+ state->pw = pw;
+
+ ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ 0, NULL, 0, &request_controls[0]);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(1, ("sss_ldap_control_create failed.\n"));
+ goto fail;
+ }
+ request_controls[1] = NULL;
+
+ DEBUG(4, ("Executing simple bind as: %s\n", state->user_dn));
+
+ ret = ldap_sasl_bind(state->sh->ldap, state->user_dn, LDAP_SASL_SIMPLE,
+ state->pw, request_controls, NULL, &msgid);
+ ldap_control_free(request_controls[0]);
+ if (ret == -1 || msgid == -1) {
+ ret = ldap_get_option(state->sh->ldap,
+ LDAP_OPT_RESULT_CODE, &ldap_err);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("ldap_bind failed (couldn't get ldap error)\n"));
+ ret = LDAP_LOCAL_ERROR;
+ } else {
+ DEBUG(1, ("ldap_bind failed (%d)[%s]\n",
+ ldap_err, ldap_err2string(ldap_err)));
+ ret = ldap_err;
+ }
+ goto fail;
+ }
+ DEBUG(8, ("ldap simple bind sent, msgid = %d\n", msgid));
+
+ if (!sh->connected) {
+ sh->connected = true;
+#ifndef HAVE_LDAP_CONNCB
+ ret = sdap_install_ldap_callbacks(sh, ev);
+ if (ret) goto fail;
+#endif
+ }
+
+ /* FIXME: get timeouts from configuration, for now 5 secs. */
+ ret = sdap_op_add(state, ev, sh, msgid,
+ simple_bind_done, req, 5, &state->op);
+ if (ret) {
+ DEBUG(1, ("Failed to set up operation!\n"));
+ goto fail;
+ }
+
+ return req;
+
+fail:
+ if (ret == LDAP_SERVER_DOWN) {
+ tevent_req_error(req, ETIMEDOUT);
+ } else {
+ tevent_req_error(req, EIO);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void simple_bind_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct simple_bind_state *state = tevent_req_data(req,
+ struct simple_bind_state);
+ char *errmsg;
+ int ret;
+ LDAPControl **response_controls;
+ int c;
+ ber_int_t pp_grace;
+ ber_int_t pp_expire;
+ LDAPPasswordPolicyError pp_error;
+
+ if (error) {
+ tevent_req_error(req, error);
+ return;
+ }
+
+ state->reply = talloc_steal(state, reply);
+
+ ret = ldap_parse_result(state->sh->ldap, state->reply->msg,
+ &state->result, NULL, &errmsg, NULL,
+ &response_controls, 0);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid));
+ ret = EIO;
+ goto done;
+ }
+
+ if (response_controls == NULL) {
+ DEBUG(5, ("Server returned no controls.\n"));
+ } else {
+ for (c = 0; response_controls[c] != NULL; c++) {
+ DEBUG(9, ("Server returned control [%s].\n",
+ response_controls[c]->ldctl_oid));
+ if (strcmp(response_controls[c]->ldctl_oid,
+ LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) {
+ ret = ldap_parse_passwordpolicy_control(state->sh->ldap,
+ response_controls[c],
+ &pp_expire, &pp_grace,
+ &pp_error);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(1, ("ldap_parse_passwordpolicy_control failed.\n"));
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(7, ("Password Policy Response: expire [%d] grace [%d] "
+ "error [%s].\n", pp_expire, pp_grace,
+ ldap_passwordpolicy_err2txt(pp_error)));
+
+ if (state->result == LDAP_SUCCESS &&
+ (pp_error == PP_changeAfterReset || pp_grace > 0)) {
+ DEBUG(4, ("User must set a new password.\n"));
+ state->result = LDAP_X_SSSD_PASSWORD_EXPIRED;
+ }
+ }
+ }
+ }
+
+ DEBUG(3, ("Bind result: %s(%d), %s\n",
+ ldap_err2string(state->result), state->result, errmsg));
+
+ ret = LDAP_SUCCESS;
+done:
+ ldap_controls_free(response_controls);
+
+ if (ret == LDAP_SUCCESS) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+}
+
+static int simple_bind_recv(struct tevent_req *req, int *ldaperr)
+{
+ struct simple_bind_state *state = tevent_req_data(req,
+ struct simple_bind_state);
+
+ *ldaperr = LDAP_OTHER;
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *ldaperr = state->result;
+ return EOK;
+}
+
+/* ==SASL-Bind============================================================ */
+
+struct sasl_bind_state {
+ struct tevent_context *ev;
+ struct sdap_handle *sh;
+
+ const char *sasl_mech;
+ const char *sasl_user;
+ struct berval *sasl_cred;
+
+ int result;
+};
+
+static int sdap_sasl_interact(LDAP *ld, unsigned flags,
+ void *defaults, void *interact);
+
+static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ const char *sasl_mech,
+ const char *sasl_user,
+ struct berval *sasl_cred)
+{
+ struct tevent_req *req;
+ struct sasl_bind_state *state;
+ int ret = EOK;
+
+ req = tevent_req_create(memctx, &state, struct sasl_bind_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->sh = sh;
+ state->sasl_mech = sasl_mech;
+ state->sasl_user = sasl_user;
+ state->sasl_cred = sasl_cred;
+
+ DEBUG(4, ("Executing sasl bind mech: %s, user: %s\n",
+ sasl_mech, sasl_user));
+
+ /* FIXME: Warning, this is a sync call!
+ * No async variant exist in openldap libraries yet */
+
+ ret = ldap_sasl_interactive_bind_s(state->sh->ldap, NULL,
+ sasl_mech, NULL, NULL,
+ LDAP_SASL_QUIET,
+ (*sdap_sasl_interact), state);
+ state->result = ret;
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(1, ("ldap_sasl_bind failed (%d)[%s]\n",
+ ret, ldap_err2string(ret)));
+ goto fail;
+ }
+
+ if (!sh->connected) {
+ sh->connected = true;
+#ifndef HAVE_LDAP_CONNCB
+ ret = sdap_install_ldap_callbacks(sh, ev);
+ if (ret) goto fail;
+#endif
+ }
+
+ tevent_req_post(req, ev);
+ return req;
+
+fail:
+ if (ret == LDAP_SERVER_DOWN) {
+ tevent_req_error(req, ETIMEDOUT);
+ } else {
+ tevent_req_error(req, EIO);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static int sdap_sasl_interact(LDAP *ld, unsigned flags,
+ void *defaults, void *interact)
+{
+ struct sasl_bind_state *state = talloc_get_type(defaults,
+ struct sasl_bind_state);
+ sasl_interact_t *in = (sasl_interact_t *)interact;
+
+ if (!ld) return LDAP_PARAM_ERROR;
+
+ while (in->id != SASL_CB_LIST_END) {
+
+ switch (in->id) {
+ case SASL_CB_GETREALM:
+ case SASL_CB_AUTHNAME:
+ case SASL_CB_PASS:
+ if (in->defresult) {
+ in->result = in->defresult;
+ } else {
+ in->result = "";
+ }
+ in->len = strlen(in->result);
+ break;
+ case SASL_CB_USER:
+ if (state->sasl_user) {
+ in->result = state->sasl_user;
+ } else if (in->defresult) {
+ in->result = in->defresult;
+ } else {
+ in->result = "";
+ }
+ in->len = strlen(in->result);
+ break;
+ case SASL_CB_NOECHOPROMPT:
+ case SASL_CB_ECHOPROMPT:
+ goto fail;
+ }
+
+ in++;
+ }
+
+ return LDAP_SUCCESS;
+
+fail:
+ return LDAP_UNAVAILABLE;
+}
+
+static int sasl_bind_recv(struct tevent_req *req, int *ldaperr)
+{
+ struct sasl_bind_state *state = tevent_req_data(req,
+ struct sasl_bind_state);
+ enum tevent_req_state tstate;
+ uint64_t err = EIO;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (tstate != TEVENT_REQ_IN_PROGRESS) {
+ *ldaperr = LDAP_OTHER;
+ return err;
+ }
+ }
+
+ *ldaperr = state->result;
+ return EOK;
+}
+
+/* ==Perform-Kinit-given-keytab-and-principal============================= */
+
+struct sdap_kinit_state {
+ int result;
+};
+
+static void sdap_kinit_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ int timeout,
+ const char *keytab,
+ const char *principal,
+ const char *realm)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct sdap_kinit_state *state;
+ int ret;
+
+ DEBUG(6, ("Attempting kinit (%s, %s, %s)\n", keytab, principal, realm));
+
+ req = tevent_req_create(memctx, &state, struct sdap_kinit_state);
+ if (!req) return NULL;
+
+ state->result = SDAP_AUTH_FAILED;
+
+ if (keytab) {
+ ret = setenv("KRB5_KTNAME", keytab, 1);
+ if (ret == -1) {
+ DEBUG(2, ("Failed to set KRB5_KTNAME to %s\n", keytab));
+ return NULL;
+ }
+ }
+
+ subreq = sdap_get_tgt_send(state, ev, realm, principal, keytab, timeout);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_kinit_done, req);
+
+ return req;
+}
+
+static void sdap_kinit_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_kinit_state *state = tevent_req_data(req,
+ struct sdap_kinit_state);
+
+ int ret;
+ int result;
+ char *ccname = NULL;
+
+ ret = sdap_get_tgt_recv(subreq, state, &result, &ccname);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ state->result = SDAP_AUTH_FAILED;
+ DEBUG(1, ("child failed (%d [%s])\n", ret, strerror(ret)));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (result == EOK) {
+ ret = setenv("KRB5CCNAME", ccname, 1);
+ if (ret == -1) {
+ DEBUG(2, ("Unable to set env. variable KRB5CCNAME!\n"));
+ state->result = SDAP_AUTH_FAILED;
+ tevent_req_error(req, EFAULT);
+ }
+
+ state->result = SDAP_AUTH_SUCCESS;
+ tevent_req_done(req);
+ return;
+ }
+
+ DEBUG(4, ("Could not get TGT: %d [%s]\n", result, strerror(result)));
+ state->result = SDAP_AUTH_FAILED;
+ tevent_req_error(req, EIO);
+}
+
+int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result)
+{
+ struct sdap_kinit_state *state = tevent_req_data(req,
+ struct sdap_kinit_state);
+ enum tevent_req_state tstate;
+ uint64_t err = EIO;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (tstate != TEVENT_REQ_IN_PROGRESS) {
+ *result = SDAP_ERROR;
+ return err;
+ }
+ }
+
+ *result = state->result;
+ return EOK;
+}
+
+
+/* ==Authenticaticate-User-by-DN========================================== */
+
+struct sdap_auth_state {
+ const char *user_dn;
+ struct berval pw;
+
+ int result;
+ bool is_sasl;
+};
+
+static void sdap_auth_done(struct tevent_req *subreq);
+
+/* TODO: handle sasl_cred */
+struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ const char *sasl_mech,
+ const char *sasl_user,
+ const char *user_dn,
+ const char *authtok_type,
+ struct dp_opt_blob authtok)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_auth_state *state;
+
+ if (authtok_type != NULL && strcasecmp(authtok_type,"password") != 0) {
+ DEBUG(1,("Authentication token type [%s] is not supported"));
+ return NULL;
+ }
+
+ req = tevent_req_create(memctx, &state, struct sdap_auth_state);
+ if (!req) return NULL;
+
+ state->user_dn = user_dn;
+ state->pw.bv_val = (char *)authtok.data;
+ state->pw.bv_len = authtok.length;
+
+ if (sasl_mech) {
+ state->is_sasl = true;
+ subreq = sasl_bind_send(state, ev, sh, sasl_mech, sasl_user, NULL);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ state->is_sasl = false;
+ subreq = simple_bind_send(state, ev, sh, user_dn, &state->pw);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ tevent_req_set_callback(subreq, sdap_auth_done, req);
+ return req;
+}
+
+static void sdap_auth_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_auth_state *state = tevent_req_data(req,
+ struct sdap_auth_state);
+ int ret;
+
+ if (state->is_sasl) {
+ ret = sasl_bind_recv(subreq, &state->result);
+ } else {
+ ret = simple_bind_recv(subreq, &state->result);
+ }
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result)
+{
+ struct sdap_auth_state *state = tevent_req_data(req,
+ struct sdap_auth_state);
+
+ *result = SDAP_ERROR;
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ switch (state->result) {
+ case LDAP_SUCCESS:
+ *result = SDAP_AUTH_SUCCESS;
+ break;
+ case LDAP_INVALID_CREDENTIALS:
+ *result = SDAP_AUTH_FAILED;
+ break;
+ case LDAP_X_SSSD_PASSWORD_EXPIRED:
+ *result = SDAP_AUTH_PW_EXPIRED;
+ break;
+ default:
+ break;
+ }
+
+ return EOK;
+}
+
+/* ==Client connect============================================ */
+
+struct sdap_cli_connect_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+ struct sdap_service *service;
+
+ bool use_rootdse;
+ struct sysdb_attrs *rootdse;
+
+ struct sdap_handle *sh;
+
+ struct fo_server *srv;
+};
+
+static void sdap_cli_resolve_done(struct tevent_req *subreq);
+static void sdap_cli_connect_done(struct tevent_req *subreq);
+static void sdap_cli_rootdse_step(struct tevent_req *req);
+static void sdap_cli_rootdse_done(struct tevent_req *subreq);
+static void sdap_cli_kinit_step(struct tevent_req *req);
+static void sdap_cli_kinit_done(struct tevent_req *subreq);
+static void sdap_cli_auth_step(struct tevent_req *req);
+static void sdap_cli_auth_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct be_ctx *be,
+ struct sdap_service *service,
+ struct sysdb_attrs **rootdse)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_cli_connect_state *state;
+
+ req = tevent_req_create(memctx, &state, struct sdap_cli_connect_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->service = service;
+ state->srv = NULL;
+
+ if (rootdse) {
+ state->use_rootdse = true;
+ state->rootdse = *rootdse;
+ } else {
+ state->use_rootdse = false;
+ state->rootdse = NULL;
+ }
+
+ /* NOTE: this call may cause service->uri to be refreshed
+ * with a new valid server. Do not use service->uri before */
+ subreq = be_resolve_server_send(state, ev, be, service->name);
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_cli_resolve_done, req);
+
+ return req;
+}
+
+static void sdap_cli_resolve_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_cli_connect_state *state = tevent_req_data(req,
+ struct sdap_cli_connect_state);
+ int ret;
+
+ ret = be_resolve_server_recv(subreq, &state->srv);
+ talloc_zfree(subreq);
+ if (ret) {
+ /* all servers have been tried and none
+ * was found good, go offline */
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ subreq = sdap_connect_send(state, state->ev, state->opts,
+ state->service->uri,
+ dp_opt_get_bool(state->opts->basic,
+ SDAP_ID_TLS));
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_cli_connect_done, req);
+}
+
+static void sdap_cli_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_cli_connect_state *state = tevent_req_data(req,
+ struct sdap_cli_connect_state);
+ const char *sasl_mech;
+ int ret;
+
+ ret = sdap_connect_recv(subreq, state, &state->sh);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->use_rootdse && !state->rootdse) {
+ /* fetch the rootDSE this time */
+ sdap_cli_rootdse_step(req);
+ return;
+ }
+
+ sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH);
+
+ if (sasl_mech && state->use_rootdse) {
+ /* check if server claims to support GSSAPI */
+ if (!sdap_rootdse_sasl_mech_is_supported(state->rootdse,
+ sasl_mech)) {
+ tevent_req_error(req, ENOTSUP);
+ return;
+ }
+ }
+
+ if (sasl_mech && (strcasecmp(sasl_mech, "GSSAPI") == 0)) {
+ if (dp_opt_get_bool(state->opts->basic, SDAP_KRB5_KINIT)) {
+ sdap_cli_kinit_step(req);
+ return;
+ }
+ }
+
+ sdap_cli_auth_step(req);
+}
+
+static void sdap_cli_rootdse_step(struct tevent_req *req)
+{
+ struct sdap_cli_connect_state *state = tevent_req_data(req,
+ struct sdap_cli_connect_state);
+ struct tevent_req *subreq;
+
+ subreq = sdap_get_rootdse_send(state, state->ev, state->opts, state->sh);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_cli_rootdse_done, req);
+
+ if (!state->sh->connected) {
+ /* this rootdse search is performed before we actually do a bind,
+ * so we need to set up the callbacks or we will never get notified
+ * of a reply */
+ state->sh->connected = true;
+#ifndef HAVE_LDAP_CONNCB
+ int ret;
+
+ ret = sdap_install_ldap_callbacks(state->sh, state->ev);
+ if (ret) {
+ tevent_req_error(req, ret);
+ }
+#endif
+ }
+}
+
+static void sdap_cli_rootdse_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_cli_connect_state *state = tevent_req_data(req,
+ struct sdap_cli_connect_state);
+ const char *sasl_mech;
+ int ret;
+
+ ret = sdap_get_rootdse_recv(subreq, state, &state->rootdse);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH);
+
+ if (sasl_mech && state->use_rootdse) {
+ /* check if server claims to support GSSAPI */
+ if (!sdap_rootdse_sasl_mech_is_supported(state->rootdse,
+ sasl_mech)) {
+ tevent_req_error(req, ENOTSUP);
+ return;
+ }
+ }
+
+ if (sasl_mech && (strcasecmp(sasl_mech, "GSSAPI") == 0)) {
+ if (dp_opt_get_bool(state->opts->basic, SDAP_KRB5_KINIT)) {
+ sdap_cli_kinit_step(req);
+ return;
+ }
+ }
+
+ sdap_cli_auth_step(req);
+}
+
+static void sdap_cli_kinit_step(struct tevent_req *req)
+{
+ struct sdap_cli_connect_state *state = tevent_req_data(req,
+ struct sdap_cli_connect_state);
+ struct tevent_req *subreq;
+
+ subreq = sdap_kinit_send(state, state->ev,
+ state->sh,
+ dp_opt_get_int(state->opts->basic,
+ SDAP_OPT_TIMEOUT),
+ dp_opt_get_string(state->opts->basic,
+ SDAP_KRB5_KEYTAB),
+ dp_opt_get_string(state->opts->basic,
+ SDAP_SASL_AUTHID),
+ dp_opt_get_string(state->opts->basic,
+ SDAP_KRB5_REALM));
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_cli_kinit_done, req);
+}
+
+static void sdap_cli_kinit_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ enum sdap_result result;
+ int ret;
+
+ ret = sdap_kinit_recv(subreq, &result);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ if (result != SDAP_AUTH_SUCCESS) {
+ tevent_req_error(req, EACCES);
+ return;
+ }
+
+ sdap_cli_auth_step(req);
+}
+
+static void sdap_cli_auth_step(struct tevent_req *req)
+{
+ struct sdap_cli_connect_state *state = tevent_req_data(req,
+ struct sdap_cli_connect_state);
+ struct tevent_req *subreq;
+
+ subreq = sdap_auth_send(state,
+ state->ev,
+ state->sh,
+ dp_opt_get_string(state->opts->basic,
+ SDAP_SASL_MECH),
+ dp_opt_get_string(state->opts->basic,
+ SDAP_SASL_AUTHID),
+ dp_opt_get_string(state->opts->basic,
+ SDAP_DEFAULT_BIND_DN),
+ dp_opt_get_string(state->opts->basic,
+ SDAP_DEFAULT_AUTHTOK_TYPE),
+ dp_opt_get_blob(state->opts->basic,
+ SDAP_DEFAULT_AUTHTOK));
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_cli_auth_done, req);
+}
+
+static void sdap_cli_auth_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ enum sdap_result result;
+ int ret;
+
+ ret = sdap_auth_recv(subreq, &result);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ if (result != SDAP_AUTH_SUCCESS) {
+ tevent_req_error(req, EACCES);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int sdap_cli_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *memctx,
+ struct sdap_handle **gsh,
+ struct sysdb_attrs **rootdse)
+{
+ struct sdap_cli_connect_state *state = tevent_req_data(req,
+ struct sdap_cli_connect_state);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ /* mark the server as bad if connection failed */
+ if (state->srv) {
+ fo_set_port_status(state->srv, PORT_NOT_WORKING);
+ }
+
+ if (tstate == TEVENT_REQ_USER_ERROR) {
+ return err;
+ }
+ return EIO;
+ } else if (state->srv) {
+ fo_set_port_status(state->srv, PORT_WORKING);
+ }
+
+ if (gsh) {
+ *gsh = talloc_steal(memctx, state->sh);
+ if (!*gsh) {
+ return ENOMEM;
+ }
+ } else {
+ talloc_zfree(state->sh);
+ }
+
+ if (rootdse) {
+ if (state->use_rootdse) {
+ *rootdse = talloc_steal(memctx, state->rootdse);
+ if (!*rootdse) {
+ return ENOMEM;
+ }
+ } else {
+ *rootdse = NULL;
+ }
+ } else {
+ talloc_zfree(rootdse);
+ }
+
+ return EOK;
+}
+
diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
new file mode 100644
index 000000000..55f76ed70
--- /dev/null
+++ b/src/providers/ldap/sdap_async_private.h
@@ -0,0 +1,68 @@
+/*
+ SSSD
+
+ Async LDAP Helper routines
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com>
+
+ 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/>.
+*/
+
+#ifndef _SDAP_ASYNC_PRIVATE_H_
+#define _SDAP_ASYNC_PRIVATE_H_
+
+#include "config.h"
+#include "providers/ldap/sdap_async.h"
+
+void make_realm_upper_case(const char *upn);
+struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx);
+
+#ifdef HAVE_LDAP_CONNCB
+int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv,
+ struct sockaddr *addr, struct ldap_conncb *ctx);
+void sdap_ldap_connect_callback_del(LDAP *ld, Sockbuf *sb,
+ struct ldap_conncb *ctx);
+#else
+int sdap_install_ldap_callbacks(struct sdap_handle *sh,
+ struct tevent_context *ev);
+#endif
+
+int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev,
+ struct sdap_handle *sh, int msgid,
+ sdap_op_callback_t *callback, void *data,
+ int timeout, struct sdap_op **_op);
+
+struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct sdap_handle *sh);
+int sdap_get_rootdse_recv(struct tevent_req *req,
+ TALLOC_CTX *memctx,
+ struct sysdb_attrs **rootdse);
+
+/* from sdap_child_helpers.c */
+
+struct tevent_req *sdap_get_tgt_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *realm_str,
+ const char *princ_str,
+ const char *keytab_name,
+ int timeout);
+
+int sdap_get_tgt_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ int *result,
+ char **ccname);
+
+#endif /* _SDAP_ASYNC_PRIVATE_H_ */
diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c
new file mode 100644
index 000000000..0a95c8a0d
--- /dev/null
+++ b/src/providers/ldap/sdap_child_helpers.c
@@ -0,0 +1,462 @@
+/*
+ SSSD
+
+ LDAP Backend Module -- child helpers
+
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ 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 <sys/types.h>
+#include <sys/wait.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "util/util.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/child_common.h"
+
+#ifndef SSSD_LIBEXEC_PATH
+#error "SSSD_LIBEXEC_PATH not defined"
+#else
+#define LDAP_CHILD SSSD_LIBEXEC_PATH"/ldap_child"
+#endif
+
+#ifndef LDAP_CHILD_USER
+#define LDAP_CHILD_USER "nobody"
+#endif
+
+struct sdap_child {
+ /* child info */
+ pid_t pid;
+ int read_from_child_fd;
+ int write_to_child_fd;
+};
+
+static void sdap_close_fd(int *fd)
+{
+ int ret;
+
+ if (*fd == -1) {
+ DEBUG(6, ("fd already closed\n"));
+ return;
+ }
+
+ ret = close(*fd);
+ if (ret) {
+ ret = errno;
+ DEBUG(2, ("Closing fd %d, return error %d (%s)\n",
+ *fd, ret, strerror(ret)));
+ }
+
+ *fd = -1;
+}
+
+static int sdap_child_destructor(void *ptr)
+{
+ struct sdap_child *child = talloc_get_type(ptr, struct sdap_child);
+
+ child_cleanup(child->read_from_child_fd, child->write_to_child_fd);
+
+ return 0;
+}
+
+static errno_t sdap_fork_child(struct sdap_child *child)
+{
+ int pipefd_to_child[2];
+ int pipefd_from_child[2];
+ pid_t pid;
+ int ret;
+ errno_t err;
+
+ ret = pipe(pipefd_from_child);
+ if (ret == -1) {
+ err = errno;
+ DEBUG(1, ("pipe failed [%d][%s].\n", err, strerror(err)));
+ return err;
+ }
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ err = errno;
+ DEBUG(1, ("pipe failed [%d][%s].\n", err, strerror(err)));
+ return err;
+ }
+
+ pid = fork();
+
+ if (pid == 0) { /* child */
+ err = exec_child(child,
+ pipefd_to_child, pipefd_from_child,
+ LDAP_CHILD, ldap_child_debug_fd);
+ if (err != EOK) {
+ DEBUG(1, ("Could not exec LDAP child: [%d][%s].\n",
+ err, strerror(err)));
+ return err;
+ }
+ } else if (pid > 0) { /* parent */
+ child->pid = pid;
+ child->read_from_child_fd = pipefd_from_child[0];
+ close(pipefd_from_child[1]);
+ child->write_to_child_fd = pipefd_to_child[1];
+ close(pipefd_to_child[0]);
+ fd_nonblocking(child->read_from_child_fd);
+ fd_nonblocking(child->write_to_child_fd);
+
+ } else { /* error */
+ err = errno;
+ DEBUG(1, ("fork failed [%d][%s].\n", err, strerror(err)));
+ return err;
+ }
+
+ return EOK;
+}
+
+static errno_t create_tgt_req_send_buffer(TALLOC_CTX *mem_ctx,
+ const char *realm_str,
+ const char *princ_str,
+ const char *keytab_name,
+ struct io_buffer **io_buf)
+{
+ struct io_buffer *buf;
+ size_t rp;
+
+ buf = talloc(mem_ctx, struct io_buffer);
+ if (buf == NULL) {
+ DEBUG(1, ("talloc failed.\n"));
+ return ENOMEM;
+ }
+
+ buf->size = 3 * sizeof(uint32_t);
+ if (realm_str) {
+ buf->size += strlen(realm_str);
+ }
+ if (princ_str) {
+ buf->size += strlen(princ_str);
+ }
+ if (keytab_name) {
+ buf->size += strlen(keytab_name);
+ }
+
+ DEBUG(7, ("buffer size: %d\n", buf->size));
+
+ buf->data = talloc_size(buf, buf->size);
+ if (buf->data == NULL) {
+ DEBUG(1, ("talloc_size failed.\n"));
+ talloc_free(buf);
+ return ENOMEM;
+ }
+
+ rp = 0;
+
+ /* realm */
+ if (realm_str) {
+ COPY_UINT32_VALUE(&buf->data[rp], strlen(realm_str), rp);
+ COPY_MEM(&buf->data[rp], realm_str, rp, strlen(realm_str));
+ } else {
+ COPY_UINT32_VALUE(&buf->data[rp], 0, rp);
+ }
+
+ /* principal */
+ if (princ_str) {
+ COPY_UINT32_VALUE(&buf->data[rp], strlen(princ_str), rp);
+ COPY_MEM(&buf->data[rp], princ_str, rp, strlen(princ_str));
+ } else {
+ COPY_UINT32_VALUE(&buf->data[rp], 0, rp);
+ }
+
+ /* keytab */
+ if (keytab_name) {
+ COPY_UINT32_VALUE(&buf->data[rp], strlen(keytab_name), rp);
+ COPY_MEM(&buf->data[rp], keytab_name, rp, strlen(realm_str));
+ } else {
+ COPY_UINT32_VALUE(&buf->data[rp], 0, rp);
+ }
+
+ *io_buf = buf;
+ return EOK;
+}
+
+static int parse_child_response(TALLOC_CTX *mem_ctx,
+ uint8_t *buf, ssize_t size,
+ int *result, char **ccache)
+{
+ size_t p = 0;
+ uint32_t len;
+ uint32_t res;
+ char *ccn;
+
+ /* operation result code */
+ COPY_UINT32_CHECK(&res, buf + p, p, size);
+
+ /* ccache name size */
+ COPY_UINT32_CHECK(&len, buf + p, p, size);
+
+ if ((p + len ) > size) return EINVAL;
+
+ ccn = talloc_size(mem_ctx, sizeof(char) * (len + 1));
+ if (ccn == NULL) {
+ DEBUG(1, ("talloc_size failed.\n"));
+ return ENOMEM;
+ }
+ memcpy(ccn, buf+p, sizeof(char) * (len + 1));
+ ccn[len] = '\0';
+
+ *result = res;
+ *ccache = ccn;
+ return EOK;
+}
+
+/* ==The-public-async-interface============================================*/
+
+struct sdap_get_tgt_state {
+ struct tevent_context *ev;
+ struct sdap_child *child;
+ ssize_t len;
+ uint8_t *buf;
+};
+
+static errno_t set_tgt_child_timeout(struct tevent_req *req,
+ struct tevent_context *ev,
+ int timeout);
+static void sdap_get_tgt_step(struct tevent_req *subreq);
+static void sdap_get_tgt_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_tgt_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *realm_str,
+ const char *princ_str,
+ const char *keytab_name,
+ int timeout)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_get_tgt_state *state;
+ struct io_buffer *buf;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sdap_get_tgt_state);
+ if (!req) {
+ return NULL;
+ }
+
+ state->ev = ev;
+
+ state->child = talloc_zero(state, struct sdap_child);
+ if (!state->child) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ state->child->read_from_child_fd = -1;
+ state->child->write_to_child_fd = -1;
+ talloc_set_destructor((TALLOC_CTX *)state->child, sdap_child_destructor);
+
+ /* prepare the data to pass to child */
+ ret = create_tgt_req_send_buffer(state,
+ realm_str, princ_str, keytab_name,
+ &buf);
+ if (ret != EOK) {
+ DEBUG(1, ("create_tgt_req_send_buffer failed.\n"));
+ goto fail;
+ }
+
+ ret = sdap_fork_child(state->child);
+ if (ret != EOK) {
+ DEBUG(1, ("sdap_fork_child failed.\n"));
+ goto fail;
+ }
+
+ ret = set_tgt_child_timeout(req, ev, timeout);
+ if (ret != EOK) {
+ DEBUG(1, ("activate_child_timeout_handler failed.\n"));
+ goto fail;
+ }
+
+ subreq = write_pipe_send(state, ev, buf->data, buf->size,
+ state->child->write_to_child_fd);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, sdap_get_tgt_step, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_get_tgt_step(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_tgt_state *state = tevent_req_data(req,
+ struct sdap_get_tgt_state);
+ int ret;
+
+ ret = write_pipe_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sdap_close_fd(&state->child->write_to_child_fd);
+
+ subreq = read_pipe_send(state, state->ev,
+ state->child->read_from_child_fd);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_tgt_done, req);
+}
+
+static void sdap_get_tgt_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_tgt_state *state = tevent_req_data(req,
+ struct sdap_get_tgt_state);
+ int ret;
+
+ ret = read_pipe_recv(subreq, state, &state->buf, &state->len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sdap_close_fd(&state->child->read_from_child_fd);
+
+ tevent_req_done(req);
+}
+
+int sdap_get_tgt_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ int *result,
+ char **ccname)
+{
+ struct sdap_get_tgt_state *state = tevent_req_data(req,
+ struct sdap_get_tgt_state);
+ char *ccn;
+ int res;
+ int ret;
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ ret = parse_child_response(mem_ctx, state->buf, state->len, &res, &ccn);
+ if (ret != EOK) {
+ DEBUG(1, ("Cannot parse child response: [%d][%s]\n", ret, strerror(ret)));
+ return ret;
+ }
+
+ DEBUG(6, ("Child responded: %d [%s]\n", res, ccn));
+ *result = res;
+ *ccname = ccn;
+ return EOK;
+}
+
+
+
+static void get_tgt_timeout_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct sdap_get_tgt_state *state = tevent_req_data(req,
+ struct sdap_get_tgt_state);
+ int ret;
+
+ DEBUG(9, ("timeout for tgt child [%d] reached.\n", state->child->pid));
+
+ ret = kill(state->child->pid, SIGKILL);
+ if (ret == -1) {
+ DEBUG(1, ("kill failed [%d][%s].\n", errno, strerror(errno)));
+ }
+
+ tevent_req_error(req, ETIMEDOUT);
+}
+
+static errno_t set_tgt_child_timeout(struct tevent_req *req,
+ struct tevent_context *ev,
+ int timeout)
+{
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ DEBUG(6, ("Setting %d seconds timeout for tgt child\n", timeout));
+
+ tv = tevent_timeval_current_ofs(timeout, 0);
+
+ te = tevent_add_timer(ev, req, tv, get_tgt_timeout_handler, req);
+ if (te == NULL) {
+ DEBUG(1, ("tevent_add_timer failed.\n"));
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+
+
+/* Setup child logging */
+int setup_child(struct sdap_id_ctx *ctx)
+{
+ int ret;
+ const char *mech;
+ struct tevent_signal *sige;
+ unsigned v;
+ FILE *debug_filep;
+
+ mech = dp_opt_get_string(ctx->opts->basic,
+ SDAP_SASL_MECH);
+ if (!mech) {
+ return EOK;
+ }
+
+ sige = tevent_add_signal(ctx->be->ev, ctx, SIGCHLD, SA_SIGINFO,
+ child_sig_handler, NULL);
+ if (sige == NULL) {
+ DEBUG(1, ("tevent_add_signal failed.\n"));
+ return ENOMEM;
+ }
+
+ if (debug_to_file != 0 && ldap_child_debug_fd == -1) {
+ ret = open_debug_file_ex("ldap_child", &debug_filep);
+ if (ret != EOK) {
+ DEBUG(0, ("Error setting up logging (%d) [%s]\n",
+ ret, strerror(ret)));
+ return ret;
+ }
+
+ ldap_child_debug_fd = fileno(debug_filep);
+ if (ldap_child_debug_fd == -1) {
+ DEBUG(0, ("fileno failed [%d][%s]\n", errno, strerror(errno)));
+ ret = errno;
+ return ret;
+ }
+
+ v = fcntl(ldap_child_debug_fd, F_GETFD, 0);
+ fcntl(ldap_child_debug_fd, F_SETFD, v & ~FD_CLOEXEC);
+ }
+
+ return EOK;
+}