diff options
-rw-r--r-- | server/Makefile.am | 3 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async.c | 3300 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async.h | 5 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async_accounts.c | 2156 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async_connection.c | 1163 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async_private.h | 44 |
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, ×tamp); - 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, ×tamp); - 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, ×tamp); + 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, ×tamp); + 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_ */ |