summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/Makefile.am3
-rw-r--r--server/providers/ldap/sdap_async.c3300
-rw-r--r--server/providers/ldap/sdap_async.h5
-rw-r--r--server/providers/ldap/sdap_async_accounts.c2156
-rw-r--r--server/providers/ldap/sdap_async_connection.c1163
-rw-r--r--server/providers/ldap/sdap_async_private.h44
6 files changed, 3386 insertions, 3285 deletions
diff --git a/server/Makefile.am b/server/Makefile.am
index 9b29350b..f2a9c96d 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -273,6 +273,7 @@ dist_noinst_HEADERS = \
providers/ldap/ldap_common.h \
providers/ldap/sdap.h \
providers/ldap/sdap_async.h \
+ providers/ldap/sdap_async_private.h \
providers/ipa/ipa_common.h \
tools/tools_util.h \
tools/sss_sync_ops.h \
@@ -515,6 +516,8 @@ libsss_ipa_la_SOURCES = \
providers/ldap/ldap_auth.c \
providers/ldap/ldap_common.c \
providers/ldap/sdap_async.c \
+ providers/ldap/sdap_async_accounts.c \
+ providers/ldap/sdap_async_connection.c \
providers/ldap/sdap.c \
util/sss_ldap.c \
util/sss_krb5.c \
diff --git a/server/providers/ldap/sdap_async.c b/server/providers/ldap/sdap_async.c
index a5d1f718..e02f3787 100644
--- a/server/providers/ldap/sdap_async.c
+++ b/server/providers/ldap/sdap_async.c
@@ -3,7 +3,7 @@
Async LDAP Helper routines
- Copyright (C) Simo Sorce <ssorce@redhat.com>
+ 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
@@ -18,21 +18,15 @@
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 <sasl/sasl.h>
-#include "db/sysdb.h"
-#include "providers/ldap/sdap_async.h"
+#include <ctype.h>
#include "util/util.h"
-#include "util/sss_krb5.h"
+#include "providers/ldap/sdap_async_private.h"
#define REALM_SEPARATOR '@'
-
-#define LDAP_X_SSSD_PASSWORD_EXPIRED 0x555D
-
#define REPLY_REALLOC_INCREMENT 10
-static void make_realm_upper_case(const char *upn)
+void make_realm_upper_case(const char *upn)
{
char *c;
@@ -74,7 +68,7 @@ static int sdap_msg_attach(TALLOC_CTX *memctx, LDAPMessage *msg)
static inline void sdap_handle_release(struct sdap_handle *sh);
static int sdap_handle_destructor(void *mem);
-static struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx)
+struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx)
{
struct sdap_handle *sh;
@@ -344,8 +338,8 @@ static void sdap_process_next_reply(struct tevent_context *ev,
op->callback(op, op->list, EOK, op->data);
}
-static int sdap_install_ldap_callbacks(struct sdap_handle *sh,
- struct tevent_context *ev)
+int sdap_install_ldap_callbacks(struct sdap_handle *sh,
+ struct tevent_context *ev)
{
int fd;
int ret;
@@ -396,10 +390,10 @@ static void sdap_op_timeout(struct tevent_req *req)
op->callback(op, NULL, ETIMEDOUT, op->data);
}
-static 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)
+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;
@@ -437,2986 +431,7 @@ static int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev,
return EOK;
}
-/* ==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,
- 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;
-
- 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,
- dp_opt_get_string(opts->basic, SDAP_URI));
- if (lret != LDAP_SUCCESS) {
- DEBUG(1, ("ldap_initialize failed: %s\n", ldap_err2string(ret)));
- 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;
- }
-
- /* 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;
- ret = sdap_install_ldap_callbacks(state->sh, state->ev);
- if (ret) goto fail;
-
- /* 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;
-
- 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;
- ret = sdap_install_ldap_callbacks(sh, ev);
- if (ret) goto fail;
- }
-
- /* 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);
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- *ldaperr = LDAP_OTHER;
- if (err) return err;
- return EIO;
- }
-
- *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;
- ret = sdap_install_ldap_callbacks(sh, ev);
- if (ret) goto fail;
- }
-
- 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;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (tstate != TEVENT_REQ_IN_PROGRESS) {
- *ldaperr = LDAP_OTHER;
- if (err) return err;
- return EIO;
- }
- }
-
- *ldaperr = state->result;
- return EOK;
-}
-
-/* ==Perform-Kinit-given-keytab-and-principal============================= */
-
-static int sdap_krb5_get_tgt_sync(TALLOC_CTX *memctx,
- const char *realm_str,
- const char *princ_str,
- const char *keytab_name)
-{
- 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;
- }
-
- ret = setenv("KRB5CCNAME", ccname, 1);
- if (ret == -1) {
- DEBUG(2, ("Unable to set env. variable KRB5CCNAME!\n"));
- ret = EFAULT;
- 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;
-
-done:
- if (keytab) krb5_kt_close(context, keytab);
- if (context) krb5_free_context(context);
- return ret;
-}
-
-struct sdap_kinit_state {
- int result;
-};
-
-/* TODO: make it really async */
-struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx,
- struct tevent_context *ev,
- struct sdap_handle *sh,
- const char *keytab,
- const char *principal,
- const char *realm)
-{
- struct tevent_req *req;
- 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));
- ret = EFAULT;
- goto fail;
- }
- }
-
- ret = sdap_krb5_get_tgt_sync(state, realm, principal, keytab);
- if (ret == EOK) {
- state->result = SDAP_AUTH_SUCCESS;
- } else {
- goto fail;
- }
-
- tevent_req_post(req, ev);
- return req;
-
-fail:
- tevent_req_error(req, ret);
- tevent_req_post(req, ev);
- return req;
-}
-
-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;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (tstate != TEVENT_REQ_IN_PROGRESS) {
- *result = SDAP_ERROR;
- if (err) return err;
- return EIO;
- }
- }
-
- *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);
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- *result = SDAP_ERROR;
- return err;
- }
- 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:
- *result = SDAP_ERROR;
- }
-
- return EOK;
-}
-
-
-/* ==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)
-{
- 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;
-
- 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;
-
- 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;
-
- 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;
- }
- }
- }
-
- 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,
- dp_opt_get_int(opts->basic,
- SDAP_ENTRY_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);
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (!err) return EIO;
- return err;
- }
-
- if (timestamp) {
- *timestamp = talloc_steal(mem_ctx, state->timestamp);
- }
-
- return EOK;
-}
-
-/* ==Group-Parsing Routines=============================================== */
-
-static int sdap_parse_memberships(TALLOC_CTX *memctx,
- struct sysdb_handle *handle,
- struct sdap_options *opts,
- struct ldb_val *values,
- int num_values,
- const char ***member_users,
- const char ***member_groups)
-{
- const char **mgs = NULL;
- const char **mus = NULL;
- int i, u, g;
- int ret;
-
- /* if this is the first time we are called, check if users and
- * groups base DNs are set, if not do it */
- if (!opts->users_base) {
- opts->users_base = ldb_dn_new_fmt(opts,
- sysdb_handle_get_ldb(handle), "%s",
- dp_opt_get_string(opts->basic,
- SDAP_USER_SEARCH_BASE));
- if (!opts->users_base) {
- DEBUG(1, ("Unable to get casefold Users Base DN from [%s]\n",
- dp_opt_get_string(opts->basic,
- SDAP_USER_SEARCH_BASE)));
- DEBUG(1, ("Out of memory?!\n"));
- ret = ENOMEM;
- goto done;
- }
- }
- if (!opts->groups_base) {
- opts->groups_base = ldb_dn_new_fmt(opts,
- sysdb_handle_get_ldb(handle), "%s",
- dp_opt_get_string(opts->basic,
- SDAP_GROUP_SEARCH_BASE));
- if (!opts->users_base) {
- DEBUG(1, ("Unable to get casefold Users Base DN from [%s]\n",
- dp_opt_get_string(opts->basic,
- SDAP_GROUP_SEARCH_BASE)));
- DEBUG(1, ("Out of memory?!\n"));
- ret = ENOMEM;
- goto done;
- }
- }
-
- switch (opts->schema_type) {
- case SDAP_SCHEMA_RFC2307:
- DEBUG(9, ("[RFC2307 Schema]\n"));
-
- mus = talloc_array(memctx, const char *, num_values +1);
- if (!mus) {
- ret = ENOMEM;
- goto done;
- }
- for (i = 0; i < num_values; i++) {
- mus[i] = (char *)values[i].data;
- DEBUG(7, (" member user %d: [%s]\n", i, mus[i]));
- }
- mus[i] = NULL;
-
- break;
-
- case SDAP_SCHEMA_RFC2307BIS:
- DEBUG(9, ("[RFC2307bis Schema]\n"));
-
- /* in this schema only users are members */
- mus = talloc_array(memctx, const char *, num_values +1);
- if (!mus) {
- ret = ENOMEM;
- goto done;
- }
-
- for (i = 0, u = 0; i < num_values; i++) {
- struct ldb_dn *tmp_dn = NULL;
- const struct ldb_val *v;
-
- /* parse out DN */
- tmp_dn = ldb_dn_new_fmt(mus,
- sysdb_handle_get_ldb(handle), "%.*s",
- (int)values[i].length,
- (char *)values[i].data);
- if (!tmp_dn) {
- DEBUG(1, ("Unable to parse DN: [%.*s]\n",
- (int)values[i].length,
- (char *)values[i].data));
- continue;
- }
- v = ldb_dn_get_rdn_val(tmp_dn);
- if (!v) {
- DEBUG(1, ("Unable to parse DN: [%.*s]\n",
- (int)values[i].length,
- (char *)values[i].data));
- continue;
- }
-
- mus[u] = talloc_asprintf(mus, "%.*s",
- (int)v->length,
- (char *)v->data);
- if (!mus[u]) {
- DEBUG(1, ("Out of memory?!\n"));
- continue;
- }
- u++;
-
- DEBUG(9, ("Member DN [%.*s], RDN [%.*s]\n",
- (int)values[i].length, (char *)values[i].data,
- (int)v->length, (char *)v->data));
- }
- break;
-
- case SDAP_SCHEMA_IPA_V1:
- case SDAP_SCHEMA_AD:
- DEBUG(9, ("[IPA or AD Schema]\n"));
-
- /* Just allocate both big enough to contain all members for now */
- mus = talloc_array(memctx, const char *, num_values +1);
- if (!mus) {
- ret = ENOMEM;
- goto done;
- }
-
- mgs = talloc_array(memctx, const char *, num_values +1);
- if (!mgs) {
- ret = ENOMEM;
- goto done;
- }
-
- u = 0;
- g = 0;
-
- for (i = 0; i < num_values; i++) {
- struct ldb_dn *tmp_dn = NULL;
- const struct ldb_val *v;
-
- /* parse out DN */
- tmp_dn = ldb_dn_new_fmt(mus,
- sysdb_handle_get_ldb(handle),
- "%.*s",
- (int)values[i].length,
- (char *)values[i].data);
- if (!tmp_dn) {
- DEBUG(1, ("Unable to parse DN: [%.*s]\n",
- (int)values[i].length,
- (char *)values[i].data));
- continue;
- }
- v = ldb_dn_get_rdn_val(tmp_dn);
- if (!v) {
- DEBUG(1, ("Unable to parse DN: [%.*s]\n",
- (int)values[i].length,
- (char *)values[i].data));
- continue;
- }
- DEBUG(9, ("Member DN [%.*s], RDN [%.*s]\n",
- (int)values[i].length, (char *)values[i].data,
- (int)v->length, (char *)v->data));
-
- if (ldb_dn_compare_base(opts->users_base, tmp_dn) == 0) {
- mus[u] = talloc_asprintf(mus, "%.*s",
- (int)v->length,
- (char *)v->data);
- if (!mus[u]) {
- DEBUG(1, ("Out of memory?!\n"));
- continue;
- }
- u++;
-
- DEBUG(7, (" member user %d: [%.*s]\n", i,
- (int)v->length, (char *)v->data));
- } else
- if (ldb_dn_compare_base(opts->groups_base, tmp_dn) == 0) {
- mgs[g] = talloc_asprintf(mgs, "%.*s",
- (int)v->length,
- (char *)v->data);
- if (!mgs[g]) {
- DEBUG(1, ("Out of memory?!\n"));
- continue;
- }
- g++;
-
- DEBUG(7, (" member group %d: [%.*s]\n", i,
- (int)v->length, (char *)v->data));
- } else {
- DEBUG(1, ("Unkown Member type for DN: [%.*s]\n",
- (int)values[i].length,
- (char *)values[i].data));
- continue;
- }
- }
-
- if (g) {
- mgs[g] = NULL;
- } else {
- talloc_zfree(mgs);
- }
-
- if (u) {
- mus[u] = NULL;
- } else {
- talloc_zfree(mus);
- }
-
- break;
-
- default:
- DEBUG(0, ("FATAL ERROR: Unhandled schema type! (%d)\n",
- opts->schema_type));
- ret = EFAULT;
- goto done;
- }
-
- ret = EOK;
-
-done:
- if (ret != EOK) {
- talloc_zfree(mus);
- talloc_zfree(mgs);
- }
-
- *member_users = mus;
- *member_groups = mgs;
-
- 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;
- const char **member_groups = NULL;
- const char **member_users = NULL;
- 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;
-
- 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_parse_memberships(state, handle, opts,
- el->values, el->num_values,
- &member_users, &member_groups);
- 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,
- member_users, member_groups,
- 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);
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (!err) return EIO;
- return err;
- }
-
- 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;
- const char **member_groups = NULL;
- const char **member_users = 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));
-
- ret = sdap_parse_memberships(state, handle, opts,
- el->values, el->num_values,
- &member_users, &member_groups);
- 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,
- member_users, member_groups,
- NULL,
- 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)
-{
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (!err) return EIO;
- return err;
- }
- 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 = 0;
- 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]);
- 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));
- }
-
- 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);
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (err) return err;
- return EIO;
- }
-
- 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);
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (err) return err;
- return EIO;
- }
-
- if (timestamp) {
- *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
- }
-
- 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 = 0;
- state->cur = 0;
- state->handle = NULL;
- state->higher_timestamp = NULL;
-
- switch (opts->schema_type) {
- case SDAP_SCHEMA_RFC2307:
- case SDAP_SCHEMA_RFC2307BIS:
- state->twopass = false;
- break;
-
- 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));
- }
-
- 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);
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (err) return err;
- return EIO;
- }
-
- 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);
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (err) return err;
- return EIO;
- }
-
- 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)
-{
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (err) return err;
- return EIO;
- }
- 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)
-{
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (err) return err;
- return EIO;
- }
- 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);
- 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);
- const char *user_dn;
- 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:
-
- ret = sysdb_attrs_get_string(state->orig_user,
- SYSDB_ORIG_DN, &user_dn);
- if (ret) {
- tevent_req_error(req, EINVAL);
- return;
- }
-
- subreq = sdap_initgr_rfc2307_send(state, state->ev, state->opts,
- state->sysdb, state->dom,
- state->sh, user_dn,
- state->name, state->grp_attrs);
- if (!subreq) {
- tevent_req_error(req, ENOMEM);
- return;
- }
- tevent_req_set_callback(subreq, sdap_get_initgr_done, req);
- return;
-
- 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:
- case SDAP_SCHEMA_RFC2307BIS:
-
- ret = sdap_initgr_rfc2307_recv(subreq);
- break;
-
- 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)
-{
- enum tevent_req_state tstate;
- uint64_t err;
-
- if (tevent_req_is_error(req, &tstate, &err)) {
- if (err) return err;
- return EIO;
- }
-
- return EOK;
-}
+/* ==Modify-Password====================================================== */
struct sdap_exop_modify_passwd_state {
struct sdap_handle *sh;
@@ -3681,9 +696,9 @@ static void sdap_get_rootdse_done(struct tevent_req *subreq)
tevent_req_done(req);
}
-static int sdap_get_rootdse_recv(struct tevent_req *req,
- TALLOC_CTX *memctx,
- struct sysdb_attrs **rootdse)
+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);
@@ -3700,291 +715,6 @@ static int sdap_get_rootdse_recv(struct tevent_req *req,
return EOK;
}
-/* ==Client connect============================================ */
-
-struct sdap_cli_connect_state {
- struct tevent_context *ev;
- struct sdap_options *opts;
-
- struct sysdb_attrs *rootdse;
- bool use_rootdse;
- struct sdap_handle *sh;
-};
-
-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 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;
- if (rootdse) {
- state->use_rootdse = true;
- state->rootdse = *rootdse;
- } else {
- state->use_rootdse = false;
- state->rootdse = NULL;
- }
-
- subreq = sdap_connect_send(state, ev, opts,
- dp_opt_get_bool(opts->basic, SDAP_ID_TLS));
- if (!subreq) {
- talloc_zfree(req);
- return NULL;
- }
- tevent_req_set_callback(subreq, sdap_cli_connect_done, req);
-
- return 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;
- int ret;
-
- 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;
- ret = sdap_install_ldap_callbacks(state->sh, state->ev);
- if (ret) {
- tevent_req_error(req, ret);
- }
- }
-}
-
-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_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)) {
- if (err) return err;
- return EIO;
- }
-
- 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;
-}
-
/* ==Generic Search============================================ */
struct sdap_get_generic_state {
diff --git a/server/providers/ldap/sdap_async.h b/server/providers/ldap/sdap_async.h
index 955dce4f..383a2fce 100644
--- a/server/providers/ldap/sdap_async.h
+++ b/server/providers/ldap/sdap_async.h
@@ -19,6 +19,9 @@
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"
@@ -112,3 +115,5 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
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/server/providers/ldap/sdap_async_accounts.c b/server/providers/ldap/sdap_async_accounts.c
new file mode 100644
index 00000000..711b4e5e
--- /dev/null
+++ b/server/providers/ldap/sdap_async_accounts.c
@@ -0,0 +1,2156 @@
+/*
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+ }
+ }
+ }
+
+ 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,
+ dp_opt_get_int(opts->basic,
+ SDAP_ENTRY_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);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (!err) return EIO;
+ return err;
+ }
+
+ 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 = 0;
+ 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]);
+ 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));
+ }
+
+ 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);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (err) return err;
+ return EIO;
+ }
+
+ 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);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (err) return err;
+ return EIO;
+ }
+
+ if (timestamp) {
+ *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
+ }
+
+ return EOK;
+}
+
+/* ==Group-Parsing Routines=============================================== */
+
+static int sdap_parse_memberships(TALLOC_CTX *memctx,
+ struct sysdb_handle *handle,
+ struct sdap_options *opts,
+ struct ldb_val *values,
+ int num_values,
+ const char ***member_users,
+ const char ***member_groups)
+{
+ const char **mgs = NULL;
+ const char **mus = NULL;
+ int i, u, g;
+ int ret;
+
+ /* if this is the first time we are called, check if users and
+ * groups base DNs are set, if not do it */
+ if (!opts->users_base) {
+ opts->users_base = ldb_dn_new_fmt(opts,
+ sysdb_handle_get_ldb(handle), "%s",
+ dp_opt_get_string(opts->basic,
+ SDAP_USER_SEARCH_BASE));
+ if (!opts->users_base) {
+ DEBUG(1, ("Unable to get casefold Users Base DN from [%s]\n",
+ dp_opt_get_string(opts->basic,
+ SDAP_USER_SEARCH_BASE)));
+ DEBUG(1, ("Out of memory?!\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ if (!opts->groups_base) {
+ opts->groups_base = ldb_dn_new_fmt(opts,
+ sysdb_handle_get_ldb(handle), "%s",
+ dp_opt_get_string(opts->basic,
+ SDAP_GROUP_SEARCH_BASE));
+ if (!opts->users_base) {
+ DEBUG(1, ("Unable to get casefold Users Base DN from [%s]\n",
+ dp_opt_get_string(opts->basic,
+ SDAP_GROUP_SEARCH_BASE)));
+ DEBUG(1, ("Out of memory?!\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ switch (opts->schema_type) {
+ case SDAP_SCHEMA_RFC2307:
+ DEBUG(9, ("[RFC2307 Schema]\n"));
+
+ mus = talloc_array(memctx, const char *, num_values +1);
+ if (!mus) {
+ ret = ENOMEM;
+ goto done;
+ }
+ for (i = 0; i < num_values; i++) {
+ mus[i] = (char *)values[i].data;
+ DEBUG(7, (" member user %d: [%s]\n", i, mus[i]));
+ }
+ mus[i] = NULL;
+
+ break;
+
+ case SDAP_SCHEMA_RFC2307BIS:
+ DEBUG(9, ("[RFC2307bis Schema]\n"));
+
+ /* in this schema only users are members */
+ mus = talloc_array(memctx, const char *, num_values +1);
+ if (!mus) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0, u = 0; i < num_values; i++) {
+ struct ldb_dn *tmp_dn = NULL;
+ const struct ldb_val *v;
+
+ /* parse out DN */
+ tmp_dn = ldb_dn_new_fmt(mus,
+ sysdb_handle_get_ldb(handle), "%.*s",
+ (int)values[i].length,
+ (char *)values[i].data);
+ if (!tmp_dn) {
+ DEBUG(1, ("Unable to parse DN: [%.*s]\n",
+ (int)values[i].length,
+ (char *)values[i].data));
+ continue;
+ }
+ v = ldb_dn_get_rdn_val(tmp_dn);
+ if (!v) {
+ DEBUG(1, ("Unable to parse DN: [%.*s]\n",
+ (int)values[i].length,
+ (char *)values[i].data));
+ continue;
+ }
+
+ mus[u] = talloc_asprintf(mus, "%.*s",
+ (int)v->length,
+ (char *)v->data);
+ if (!mus[u]) {
+ DEBUG(1, ("Out of memory?!\n"));
+ continue;
+ }
+ u++;
+
+ DEBUG(9, ("Member DN [%.*s], RDN [%.*s]\n",
+ (int)values[i].length, (char *)values[i].data,
+ (int)v->length, (char *)v->data));
+ }
+ break;
+
+ case SDAP_SCHEMA_IPA_V1:
+ case SDAP_SCHEMA_AD:
+ DEBUG(9, ("[IPA or AD Schema]\n"));
+
+ /* Just allocate both big enough to contain all members for now */
+ mus = talloc_array(memctx, const char *, num_values +1);
+ if (!mus) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ mgs = talloc_array(memctx, const char *, num_values +1);
+ if (!mgs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ u = 0;
+ g = 0;
+
+ for (i = 0; i < num_values; i++) {
+ struct ldb_dn *tmp_dn = NULL;
+ const struct ldb_val *v;
+
+ /* parse out DN */
+ tmp_dn = ldb_dn_new_fmt(mus,
+ sysdb_handle_get_ldb(handle),
+ "%.*s",
+ (int)values[i].length,
+ (char *)values[i].data);
+ if (!tmp_dn) {
+ DEBUG(1, ("Unable to parse DN: [%.*s]\n",
+ (int)values[i].length,
+ (char *)values[i].data));
+ continue;
+ }
+ v = ldb_dn_get_rdn_val(tmp_dn);
+ if (!v) {
+ DEBUG(1, ("Unable to parse DN: [%.*s]\n",
+ (int)values[i].length,
+ (char *)values[i].data));
+ continue;
+ }
+ DEBUG(9, ("Member DN [%.*s], RDN [%.*s]\n",
+ (int)values[i].length, (char *)values[i].data,
+ (int)v->length, (char *)v->data));
+
+ if (ldb_dn_compare_base(opts->users_base, tmp_dn) == 0) {
+ mus[u] = talloc_asprintf(mus, "%.*s",
+ (int)v->length,
+ (char *)v->data);
+ if (!mus[u]) {
+ DEBUG(1, ("Out of memory?!\n"));
+ continue;
+ }
+ u++;
+
+ DEBUG(7, (" member user %d: [%.*s]\n", i,
+ (int)v->length, (char *)v->data));
+ } else
+ if (ldb_dn_compare_base(opts->groups_base, tmp_dn) == 0) {
+ mgs[g] = talloc_asprintf(mgs, "%.*s",
+ (int)v->length,
+ (char *)v->data);
+ if (!mgs[g]) {
+ DEBUG(1, ("Out of memory?!\n"));
+ continue;
+ }
+ g++;
+
+ DEBUG(7, (" member group %d: [%.*s]\n", i,
+ (int)v->length, (char *)v->data));
+ } else {
+ DEBUG(1, ("Unkown Member type for DN: [%.*s]\n",
+ (int)values[i].length,
+ (char *)values[i].data));
+ continue;
+ }
+ }
+
+ if (g) {
+ mgs[g] = NULL;
+ } else {
+ talloc_zfree(mgs);
+ }
+
+ if (u) {
+ mus[u] = NULL;
+ } else {
+ talloc_zfree(mus);
+ }
+
+ break;
+
+ default:
+ DEBUG(0, ("FATAL ERROR: Unhandled schema type! (%d)\n",
+ opts->schema_type));
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_zfree(mus);
+ talloc_zfree(mgs);
+ }
+
+ *member_users = mus;
+ *member_groups = mgs;
+
+ 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;
+ const char **member_groups = NULL;
+ const char **member_users = NULL;
+ 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;
+
+ 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_parse_memberships(state, handle, opts,
+ el->values, el->num_values,
+ &member_users, &member_groups);
+ 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,
+ member_users, member_groups,
+ 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);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (!err) return EIO;
+ return err;
+ }
+
+ 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;
+ const char **member_groups = NULL;
+ const char **member_users = 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));
+
+ ret = sdap_parse_memberships(state, handle, opts,
+ el->values, el->num_values,
+ &member_users, &member_groups);
+ 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,
+ member_users, member_groups,
+ NULL,
+ 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)
+{
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (!err) return EIO;
+ return err;
+ }
+ 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 = 0;
+ state->cur = 0;
+ state->handle = NULL;
+ state->higher_timestamp = NULL;
+
+ switch (opts->schema_type) {
+ case SDAP_SCHEMA_RFC2307:
+ case SDAP_SCHEMA_RFC2307BIS:
+ state->twopass = false;
+ break;
+
+ 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));
+ }
+
+ 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);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (err) return err;
+ return EIO;
+ }
+
+ 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);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (err) return err;
+ return EIO;
+ }
+
+ 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)
+{
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (err) return err;
+ return EIO;
+ }
+ 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)
+{
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (err) return err;
+ return EIO;
+ }
+ 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);
+ 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);
+ const char *user_dn;
+ 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:
+
+ ret = sysdb_attrs_get_string(state->orig_user,
+ SYSDB_ORIG_DN, &user_dn);
+ if (ret) {
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ subreq = sdap_initgr_rfc2307_send(state, state->ev, state->opts,
+ state->sysdb, state->dom,
+ state->sh, user_dn,
+ state->name, state->grp_attrs);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_initgr_done, req);
+ return;
+
+ 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:
+ case SDAP_SCHEMA_RFC2307BIS:
+
+ ret = sdap_initgr_rfc2307_recv(subreq);
+ break;
+
+ 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)
+{
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (err) return err;
+ return EIO;
+ }
+
+ return EOK;
+}
+
diff --git a/server/providers/ldap/sdap_async_connection.c b/server/providers/ldap/sdap_async_connection.c
new file mode 100644
index 00000000..5adb7c38
--- /dev/null
+++ b/server/providers/ldap/sdap_async_connection.c
@@ -0,0 +1,1163 @@
+/*
+ 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,
+ 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;
+
+ 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,
+ dp_opt_get_string(opts->basic, SDAP_URI));
+ if (lret != LDAP_SUCCESS) {
+ DEBUG(1, ("ldap_initialize failed: %s\n", ldap_err2string(ret)));
+ 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;
+ }
+
+ /* 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;
+ ret = sdap_install_ldap_callbacks(state->sh, state->ev);
+ if (ret) goto fail;
+
+ /* 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;
+
+ 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;
+ ret = sdap_install_ldap_callbacks(sh, ev);
+ if (ret) goto fail;
+ }
+
+ /* 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);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ *ldaperr = LDAP_OTHER;
+ if (err) return err;
+ return EIO;
+ }
+
+ *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;
+ ret = sdap_install_ldap_callbacks(sh, ev);
+ if (ret) goto fail;
+ }
+
+ 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;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (tstate != TEVENT_REQ_IN_PROGRESS) {
+ *ldaperr = LDAP_OTHER;
+ if (err) return err;
+ return EIO;
+ }
+ }
+
+ *ldaperr = state->result;
+ return EOK;
+}
+
+/* ==Perform-Kinit-given-keytab-and-principal============================= */
+
+static int sdap_krb5_get_tgt_sync(TALLOC_CTX *memctx,
+ const char *realm_str,
+ const char *princ_str,
+ const char *keytab_name)
+{
+ 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;
+ }
+
+ ret = setenv("KRB5CCNAME", ccname, 1);
+ if (ret == -1) {
+ DEBUG(2, ("Unable to set env. variable KRB5CCNAME!\n"));
+ ret = EFAULT;
+ 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;
+
+done:
+ if (keytab) krb5_kt_close(context, keytab);
+ if (context) krb5_free_context(context);
+ return ret;
+}
+
+struct sdap_kinit_state {
+ int result;
+};
+
+/* TODO: make it really async */
+struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ const char *keytab,
+ const char *principal,
+ const char *realm)
+{
+ struct tevent_req *req;
+ 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));
+ ret = EFAULT;
+ goto fail;
+ }
+ }
+
+ ret = sdap_krb5_get_tgt_sync(state, realm, principal, keytab);
+ if (ret == EOK) {
+ state->result = SDAP_AUTH_SUCCESS;
+ } else {
+ goto fail;
+ }
+
+ tevent_req_post(req, ev);
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+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;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ if (tstate != TEVENT_REQ_IN_PROGRESS) {
+ *result = SDAP_ERROR;
+ if (err) return err;
+ return EIO;
+ }
+ }
+
+ *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);
+ enum tevent_req_state tstate;
+ uint64_t err;
+
+ if (tevent_req_is_error(req, &tstate, &err)) {
+ *result = SDAP_ERROR;
+ return err;
+ }
+ 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:
+ *result = SDAP_ERROR;
+ }
+
+ return EOK;
+}
+
+/* ==Client connect============================================ */
+
+struct sdap_cli_connect_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+
+ struct sysdb_attrs *rootdse;
+ bool use_rootdse;
+ struct sdap_handle *sh;
+};
+
+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 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;
+ if (rootdse) {
+ state->use_rootdse = true;
+ state->rootdse = *rootdse;
+ } else {
+ state->use_rootdse = false;
+ state->rootdse = NULL;
+ }
+
+ subreq = sdap_connect_send(state, ev, opts,
+ dp_opt_get_bool(opts->basic, SDAP_ID_TLS));
+ if (!subreq) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, sdap_cli_connect_done, req);
+
+ return 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;
+ int ret;
+
+ 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;
+ ret = sdap_install_ldap_callbacks(state->sh, state->ev);
+ if (ret) {
+ tevent_req_error(req, ret);
+ }
+ }
+}
+
+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_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)) {
+ if (err) return err;
+ return EIO;
+ }
+
+ 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/server/providers/ldap/sdap_async_private.h b/server/providers/ldap/sdap_async_private.h
new file mode 100644
index 00000000..3d891531
--- /dev/null
+++ b/server/providers/ldap/sdap_async_private.h
@@ -0,0 +1,44 @@
+/*
+ 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 "providers/ldap/sdap_async.h"
+
+void make_realm_upper_case(const char *upn);
+struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx);
+int sdap_install_ldap_callbacks(struct sdap_handle *sh,
+ struct tevent_context *ev);
+
+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);
+#endif /* _SDAP_ASYNC_PRIVATE_H_ */