diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/Makefile.am | 6 | ||||
-rw-r--r-- | server/db/sysdb.h | 7 | ||||
-rw-r--r-- | server/db/sysdb_ops.c | 37 | ||||
-rw-r--r-- | server/providers/ldap/sdap.c | 367 | ||||
-rw-r--r-- | server/providers/ldap/sdap.h | 132 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async.c | 1446 | ||||
-rw-r--r-- | server/providers/ldap/sdap_async.h | 90 |
7 files changed, 2060 insertions, 25 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index 0627836d..63d98bcd 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -212,6 +212,8 @@ dist_noinst_HEADERS = \ providers/dp_backend.h \ providers/providers.h \ providers/krb5/krb5_auth.h \ + providers/ldap/sdap.h \ + providers/ldap/sdap_async.h \ tools/tools_util.h \ krb5_plugin/sssd_krb5_locator_plugin.h \ $(infopipe_headers) \ @@ -381,7 +383,9 @@ endif #BUILD_TESTS #################### libsss_ldap_la_SOURCES = \ providers/ldap/ldap_id.c \ - providers/ldap/ldap_auth.c + providers/ldap/ldap_auth.c \ + providers/ldap/sdap_async.c \ + providers/ldap/sdap.c libsss_ldap_la_CFLAGS = \ $(AM_CFLAGS) \ $(LDAP_CFLAGS) diff --git a/server/db/sysdb.h b/server/db/sysdb.h index 35a17ddd..aa1aaf4c 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -65,6 +65,10 @@ #define SYSDB_CACHEDPWD "cachedPassword" +#define SYSDB_ORIG_DN "originalDN" +#define SYSDB_UUID "uniqueID" +#define SYSDB_UPN "UserPrincipalName" + #define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)" #define SYSDB_UC "objectclass="SYSDB_USER_CLASS @@ -139,6 +143,8 @@ struct sysdb_attrs { /* sysdb_attrs helper functions */ struct sysdb_attrs *sysdb_new_attrs(TALLOC_CTX *memctx); + +/* values are copied in the structure, allocated on "attrs" */ int sysdb_attrs_add_val(struct sysdb_attrs *attrs, const char *name, const struct ldb_val *val); int sysdb_attrs_add_string(struct sysdb_attrs *attrs, @@ -269,6 +275,7 @@ struct tevent_req *sysdb_search_entry_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct ldb_dn *base_dn, + int scope, const char *filter, const char **attrs); int sysdb_search_entry_recv(struct tevent_req *req, diff --git a/server/db/sysdb_ops.c b/server/db/sysdb_ops.c index 785ce15e..24d90b12 100644 --- a/server/db/sysdb_ops.c +++ b/server/db/sysdb_ops.c @@ -297,6 +297,7 @@ struct tevent_req *sysdb_search_entry_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct ldb_dn *base_dn, + int scope, const char *filter, const char **attrs) { @@ -314,8 +315,8 @@ struct tevent_req *sysdb_search_entry_send(TALLOC_CTX *mem_ctx, state->ldbreply = NULL; ret = ldb_build_search_req(&ldbreq, handle->ctx->ldb, state, - base_dn, LDB_SCOPE_SUBTREE, - filter, attrs, NULL, NULL, NULL, NULL); + base_dn, scope, filter, attrs, + NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); @@ -423,7 +424,6 @@ struct tevent_req *sysdb_search_user_by_name_send(TALLOC_CTX *mem_ctx, struct sysdb_search_user_state *state; static const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; struct ldb_dn *base_dn; - char *filter; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); @@ -433,17 +433,12 @@ struct tevent_req *sysdb_search_user_by_name_send(TALLOC_CTX *mem_ctx, state->handle = handle; state->msg = NULL; - base_dn = ldb_dn_new_fmt(state, handle->ctx->ldb, - SYSDB_TMPL_USER_BASE, domain->name); + base_dn = sysdb_user_dn(handle->ctx, state, domain->name, name); if (!base_dn) ERROR_OUT(ret, ENOMEM, fail); - filter = talloc_asprintf(state, SYSDB_PWNAM_FILTER, name); - if (!filter) - ERROR_OUT(ret, ENOMEM, fail); - - subreq = sysdb_search_entry_send(state, ev, handle, - base_dn, filter, attrs); + subreq = sysdb_search_entry_send(state, ev, handle, base_dn, + LDB_SCOPE_BASE, NULL, attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } @@ -486,8 +481,8 @@ struct tevent_req *sysdb_search_user_by_uid_send(TALLOC_CTX *mem_ctx, if (!filter) ERROR_OUT(ret, ENOMEM, fail); - subreq = sysdb_search_entry_send(state, ev, handle, - base_dn, filter, attrs); + subreq = sysdb_search_entry_send(state, ev, handle, base_dn, + LDB_SCOPE_ONELEVEL, filter, attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } @@ -640,7 +635,6 @@ struct tevent_req *sysdb_search_group_by_name_send(TALLOC_CTX *mem_ctx, struct sysdb_search_group_state *state; static const char *attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; struct ldb_dn *base_dn; - char *filter; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); @@ -650,17 +644,12 @@ struct tevent_req *sysdb_search_group_by_name_send(TALLOC_CTX *mem_ctx, state->handle = handle; state->msg = NULL; - base_dn = ldb_dn_new_fmt(state, handle->ctx->ldb, - SYSDB_TMPL_GROUP_BASE, domain->name); + base_dn = sysdb_group_dn(handle->ctx, state, domain->name, name); if (!base_dn) ERROR_OUT(ret, ENOMEM, fail); - filter = talloc_asprintf(state, SYSDB_GRNAM_FILTER, name); - if (!filter) - ERROR_OUT(ret, ENOMEM, fail); - - subreq = sysdb_search_entry_send(state, ev, handle, - base_dn, filter, attrs); + subreq = sysdb_search_entry_send(state, ev, handle, base_dn, + LDB_SCOPE_BASE, NULL, attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } @@ -703,8 +692,8 @@ struct tevent_req *sysdb_search_group_by_gid_send(TALLOC_CTX *mem_ctx, if (!filter) ERROR_OUT(ret, ENOMEM, fail); - subreq = sysdb_search_entry_send(state, ev, handle, - base_dn, filter, attrs); + subreq = sysdb_search_entry_send(state, ev, handle, base_dn, + LDB_SCOPE_ONELEVEL, filter, attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } diff --git a/server/providers/ldap/sdap.c b/server/providers/ldap/sdap.c new file mode 100644 index 00000000..9abcf566 --- /dev/null +++ b/server/providers/ldap/sdap.c @@ -0,0 +1,367 @@ +/* + SSSD + + LDAP Helper routines + + Copyright (C) Simo Sorce <ssorce@redhat.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define LDAP_DEPRECATED 1 +#include <ldap.h> +#include "util/util.h" +#include "confdb/confdb.h" +#include "providers/ldap/sdap.h" + +struct sdap_gen_opts default_basic_opts[] = { + { "ldapUri", "ldap://localhost", NULL }, + { "defaultBindDn", NULL, NULL }, + { "defaultAuthtokType", NULL, NULL }, + { "defaultAuthtok", NULL, NULL }, + { "network_timeout", "5", NULL }, + { "opt_timeout", "5", NULL }, + { "tls_reqcert", "always", NULL }, + { "userSearchBase", "dc=example,dc=com", NULL }, + { "userSearchScope", "sub", NULL }, + { "userSearchFilter", NULL, NULL }, + { "groupSearchBase", "dc=example,dc=com", NULL }, + { "groupSearchScope", "sub", NULL }, + { "groupSearchFilter", NULL, NULL }, + { "ldapSchema", "rfc2307", NULL }, + { "offline_timeout", "5", NULL } +}; + +struct sdap_id_map default_user_map[] = { + { "userObjectClass", "posixAccount", SYSDB_USER_CLASS, NULL }, + { "userName", "uid", SYSDB_NAME, NULL }, + { "userPwd", "userPassword", SYSDB_PWD, NULL }, + { "userUidNumber", "uidNumber", SYSDB_UIDNUM, NULL }, + { "userGidNumber", "gidNumber", SYSDB_GIDNUM, NULL }, + { "userGecos", "gecos", SYSDB_GECOS, NULL }, + { "userHomeDirectory", "homeDirectory", SYSDB_HOMEDIR, NULL }, + { "userShell", "loginShell", SYSDB_SHELL, NULL }, + { "userUUID", "nsUniqueId", SYSDB_UUID, NULL }, + { "userPrincipal", "krbPrincipalName", SYSDB_UPN, NULL }, + { "userFullname", "cn", SYSDB_FULLNAME, NULL }, + { "userMemberOf", "memberOf", SYSDB_MEMBEROF, NULL } +}; + +struct sdap_id_map default_group_map[] = { + { "groupObjectClass", "posixGroup", SYSDB_GROUP_CLASS, NULL }, + { "groupName", "cn", SYSDB_NAME, NULL }, + { "groupPwd", "userPassword", SYSDB_PWD, NULL }, + { "groupGidNumber", "gidNumber", SYSDB_GIDNUM, NULL }, + { "groupMember", "memberuid", SYSDB_LEGACY_MEMBER, NULL }, + { "groupUUID", "nsUniqueId", SYSDB_UUID, NULL } +}; + +/* =Retrieve-Options====================================================== */ + +int sdap_get_options(TALLOC_CTX *memctx, + struct confdb_ctx *cdb, + const char *conf_path, + struct sdap_options **_opts) +{ + struct sdap_options *opts; + int i, ret; + + opts = talloc(memctx, struct sdap_options); + if (!opts) return ENOMEM; + + opts->basic = talloc_array(opts, struct sdap_gen_opts, SDAP_OPTS_BASIC); + if (!opts) return ENOMEM; + + opts->user_map = talloc_array(opts, struct sdap_id_map, SDAP_OPTS_USER); + if (!opts) return ENOMEM; + + opts->group_map = talloc_array(opts, struct sdap_id_map, SDAP_OPTS_GROUP); + if (!opts) return ENOMEM; + + for (i = 0; i < SDAP_OPTS_BASIC; i++) { + + opts->basic[i].opt_name = default_basic_opts[i].opt_name; + opts->basic[i].def_value = default_basic_opts[i].def_value; + + ret = confdb_get_string(cdb, opts, conf_path, + opts->basic[i].opt_name, + opts->basic[i].def_value, + &opts->basic[i].value); + if (ret != EOK || + (opts->basic[i].def_value && !opts->basic[i].value)) { + DEBUG(0, ("Failed to retrieve a value (%s)\n", + opts->basic[i].opt_name)); + if (ret != EOK) ret = EINVAL; + goto done; + } + + DEBUG(5, ("Option %s has value %s\n", + opts->basic[i].opt_name, opts->basic[i].value)); + } + + for (i = 0; i < SDAP_OPTS_USER; i++) { + + opts->user_map[i].opt_name = default_user_map[i].opt_name; + opts->user_map[i].def_name = default_user_map[i].def_name; + opts->user_map[i].sys_name = default_user_map[i].sys_name; + + ret = confdb_get_string(cdb, opts, conf_path, + opts->user_map[i].opt_name, + opts->user_map[i].def_name, + &opts->user_map[i].name); + if (ret != EOK || + (opts->user_map[i].def_name && !opts->user_map[i].name)) { + DEBUG(0, ("Failed to retrieve a value (%s)\n", + opts->user_map[i].opt_name)); + if (ret != EOK) ret = EINVAL; + goto done; + } + + DEBUG(5, ("Option %s has value %s\n", + opts->user_map[i].opt_name, opts->user_map[i].name)); + } + + for (i = 0; i < SDAP_OPTS_GROUP; i++) { + + opts->group_map[i].opt_name = default_group_map[i].opt_name; + opts->group_map[i].def_name = default_group_map[i].def_name; + opts->group_map[i].sys_name = default_group_map[i].sys_name; + + ret = confdb_get_string(cdb, opts, conf_path, + opts->group_map[i].opt_name, + opts->group_map[i].def_name, + &opts->group_map[i].name); + if (ret != EOK || + (opts->group_map[i].def_name && !opts->group_map[i].name)) { + DEBUG(0, ("Failed to retrieve a value (%s)\n", + opts->group_map[i].opt_name)); + if (ret != EOK) ret = EINVAL; + goto done; + } + + DEBUG(5, ("Option %s has value %s\n", + opts->group_map[i].opt_name, opts->group_map[i].name)); + } + + /* re-read special options that are easier to be consumed after they are + * transformed */ + +/* TODO: better to have a blob object than a string here */ + ret = confdb_get_string(cdb, opts, conf_path, + "defaultAuthtok", NULL, + &opts->default_authtok); + if (ret != EOK) goto done; + if (opts->default_authtok) { + opts->default_authtok_size = strlen(opts->default_authtok); + } + + ret = confdb_get_int(cdb, opts, conf_path, + "network_timeout", 5, + &opts->network_timeout); + if (ret != EOK) goto done; + + ret = confdb_get_int(cdb, opts, conf_path, + "opt_timeout", 5, + &opts->opt_timeout); + if (ret != EOK) goto done; + + ret = confdb_get_int(cdb, opts, conf_path, + "offline_timeout", 60, + &opts->offline_timeout); + if (ret != EOK) goto done; + + ret = EOK; + *_opts = opts; + +done: + if (ret != EOK) talloc_zfree(opts); + return ret; +} + +/* =Parse-msg============================================================= */ + +static int sdap_parse_entry(TALLOC_CTX *memctx, + struct sdap_handle *sh, struct sdap_msg *sm, + struct sdap_id_map *map, int attrs_num, + struct sysdb_attrs **_attrs, char **_dn) +{ + struct sysdb_attrs *attrs; + BerElement *ber = NULL; + char **vals; + char *str; + int lerrno; + int a, i, ret; + + lerrno = 0; + ldap_set_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + + attrs = sysdb_new_attrs(memctx); + if (!attrs) return ENOMEM; + + str = ldap_get_dn(sh->ldap, sm->msg); + if (!str) { + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + DEBUG(1, ("ldap_get_dn failed: %d(%s)\n", + lerrno, ldap_err2string(lerrno))); + ret = EIO; + goto fail; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, str); + if (ret) goto fail; + if (_dn) { + *_dn = talloc_strdup(memctx, str); + if (!*_dn) { + ret = ENOMEM; + ldap_memfree(str); + goto fail; + } + } + ldap_memfree(str); + + vals = ldap_get_values(sh->ldap, sm->msg, "objectClass"); + if (!vals) { + DEBUG(1, ("Unknown entry type, no objectClasses found!\n")); + ret = EINVAL; + goto fail; + } + + for (i = 0; vals[i]; i++) { + /* the objectclass is always the first name in the map */ + if (strcasecmp(vals[i], map[0].name) == 0) { + /* ok it's a user */ + break; + } + } + if (!vals[i]) { + DEBUG(1, ("Not a user entry, objectClass not matching: %s\n", + map[0].name)); + ldap_value_free(vals); + ret = EINVAL; + goto fail; + } + ldap_value_free(vals); + + str = ldap_first_attribute(sh->ldap, sm->msg, &ber); + if (!str) { + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + DEBUG(1, ("Entry has no attributes [%d(%s)]!?\n", + lerrno, ldap_err2string(lerrno))); + ret = EINVAL; + goto fail; + } + while (str) { + for (a = 1; a < attrs_num; a++) { + /* check if it is an attr we are interested in */ + if (strcasecmp(str, map[a].name) == 0) break; + } + if (a < attrs_num) { + /* interesting attr */ + + vals = ldap_get_values(sh->ldap, sm->msg, str); + if (!vals) { + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + DEBUG(1, ("LDAP Library error: %d(%s)", + lerrno, ldap_err2string(lerrno))); + ret = EIO; + goto fail; + } + if (!vals[0]) { + DEBUG(1, ("Missing value after ldap_get_values() ??\n")); + ret = EINVAL; + goto fail; + } + for (i = 0; vals[i]; i++) { + ret = sysdb_attrs_add_string(attrs, map[a].sys_name, vals[i]); + if (ret) goto fail; + } + ldap_value_free(vals); + } + + ldap_memfree(str); + str = ldap_next_attribute(sh->ldap, sm->msg, ber); + } + ber_free(ber, 0); + + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + if (lerrno) { + DEBUG(1, ("LDAP Library error: %d(%s)", + lerrno, ldap_err2string(lerrno))); + ret = EIO; + goto fail; + } + + *_attrs = attrs; + return EOK; + +fail: + if (ber) ber_free(ber, 0); + talloc_free(attrs); + return ret; +} + +/* This function converts an ldap message into a sysdb_attrs structure. + * It converts only known user attributes, the rest are ignored. + * If the entry is not that of an user an error is returned. + * The original DN is stored as an attribute named originalDN */ + +int sdap_parse_user(TALLOC_CTX *memctx, struct sdap_options *opts, + struct sdap_handle *sh, struct sdap_msg *sm, + struct sysdb_attrs **_attrs, char **_dn) +{ + + return sdap_parse_entry(memctx, sh, sm, opts->user_map, + SDAP_OPTS_USER, _attrs, _dn); +} + +/* This function converts an ldap message into a sysdb_attrs structure. + * It converts only known group attributes, the rest are ignored. + * If the entry is not that of an user an error is returned. + * The original DN is stored as an attribute named originalDN */ + +int sdap_parse_group(TALLOC_CTX *memctx, struct sdap_options *opts, + struct sdap_handle *sh, struct sdap_msg *sm, + struct sysdb_attrs **_attrs, char **_dn) +{ + + return sdap_parse_entry(memctx, sh, sm, opts->group_map, + SDAP_OPTS_GROUP, _attrs, _dn); +} + +/* =Get-DN-from-message=================================================== */ + +int sdap_get_msg_dn(TALLOC_CTX *memctx, struct sdap_handle *sh, + struct sdap_msg *sm, char **_dn) +{ + char *str; + int lerrno; + + lerrno = 0; + ldap_set_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + + str = ldap_get_dn(sh->ldap, sm->msg); + if (!str) { + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + DEBUG(1, ("ldap_get_dn failed: %d(%s)\n", + lerrno, ldap_err2string(lerrno))); + return EIO; + } + + *_dn = talloc_strdup(memctx, str); + ldap_memfree(str); + if (!*_dn) return ENOMEM; + + return EOK; +} + diff --git a/server/providers/ldap/sdap.h b/server/providers/ldap/sdap.h new file mode 100644 index 00000000..b3435c8b --- /dev/null +++ b/server/providers/ldap/sdap.h @@ -0,0 +1,132 @@ +/* + SSSD + + LDAP Helper routines + + Copyright (C) Simo Sorce <ssorce@redhat.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "confdb/confdb.h" +#include "db/sysdb.h" +#include <ldap.h> + +struct sdap_handle { + LDAP *ldap; + bool connected; + int fd; +}; + +enum sdap_result { + SDAP_SUCCESS, + SDAP_NOT_FOUND, + SDAP_UNAVAIL, + SDAP_RETRY, + SDAP_ERROR, + SDAP_AUTH_SUCCESS, + SDAP_AUTH_FAILED +}; + +struct sdap_msg { + LDAPMessage *msg; +}; + +#define SDAP_URI 0 +#define SDAP_DEFAULT_BIND_DN 1 +#define SDAP_DEFAULT_AUTHTOK_TYPE 2 +#define SDAP_DEFAULT_AUTHTOK 3 +#define SDAP_NETWROK_TIMEOUT 4 +#define SDAP_OPT_TIMEOUT 5 +#define SDAP_TLS_REQCERT 6 +#define SDAP_USER_SEARCH_BASE 7 +#define SDAP_USER_SEARCH_SCOPE 8 +#define SDAP_USER_SEARCH_FILTER 9 +#define SDAP_GROUP_SEARCH_BASE 10 +#define SDAP_GROUP_SEARCH_SCOPE 11 +#define SDAP_GROUP_SEARCH_FILTER 12 +#define SDAP_SCHEMA 13 +#define SDAP_OFFLINE_TIMEOUT 14 + +#define SDAP_OPTS_BASIC 15 /* opts counter */ + +/* the objectclass must be the first attribute. + * Functions depend on this */ +#define SDAP_OC_USER 0 +#define SDAP_AT_USER_NAME 1 +#define SDAP_AT_USER_PWD 2 +#define SDAP_AT_USER_UID 3 +#define SDAP_AT_USER_GID 4 +#define SDAP_AT_USER_GECOS 5 +#define SDAP_AT_USER_HOME 6 +#define SDAP_AT_USER_SHELL 7 +#define SDAP_AT_USER_UUID 8 +#define SDAP_AT_USER_PRINC 9 +#define SDAP_AT_USER_FULLNAME 10 +#define SDAP_AT_USER_MEMBEROF 11 + +#define SDAP_OPTS_USER 12 /* attrs counter */ + +/* the objectclass must be the first attribute. + * Functions depend on this */ +#define SDAP_OC_GROUP 0 +#define SDAP_AT_GROUP_NAME 1 +#define SDAP_AT_GROUP_PWD 2 +#define SDAP_AT_GROUP_GID 3 +#define SDAP_AT_GROUP_MEMBER 4 +#define SDAP_AT_GROUP_UUID 5 + +#define SDAP_OPTS_GROUP 6 /* attrs counter */ + +struct sdap_gen_opts { + const char *opt_name; + const char *def_value; + char *value; +}; + +struct sdap_id_map { + const char *opt_name; + const char *def_name; + const char *sys_name; + char *name; +}; + +struct sdap_options { + struct sdap_gen_opts *basic; + struct sdap_id_map *user_map; + struct sdap_id_map *group_map; + + /* transformed for easier consumption */ + uint32_t default_authtok_size; + char *default_authtok; /* todo: turn into uint8_t */ + int network_timeout; + int opt_timeout; + int offline_timeout; +}; + +int sdap_get_options(TALLOC_CTX *memctx, + struct confdb_ctx *cdb, + const char *conf_path, + struct sdap_options **_opts); + +int sdap_parse_user(TALLOC_CTX *memctx, struct sdap_options *opts, + struct sdap_handle *sh, struct sdap_msg *sm, + struct sysdb_attrs **_attrs, char **_dn); + +int sdap_parse_group(TALLOC_CTX *memctx, struct sdap_options *opts, + struct sdap_handle *sh, struct sdap_msg *sm, + struct sysdb_attrs **_attrs, char **_dn); + +int sdap_get_msg_dn(TALLOC_CTX *memctx, struct sdap_handle *sh, + struct sdap_msg *sm, char **_dn); diff --git a/server/providers/ldap/sdap_async.c b/server/providers/ldap/sdap_async.c new file mode 100644 index 00000000..e618824d --- /dev/null +++ b/server/providers/ldap/sdap_async.c @@ -0,0 +1,1446 @@ +/* + 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/>. +*/ + +#include "db/sysdb.h" +#include "providers/ldap/sdap_async.h" +#include "util/util.h" + +/* ==LDAP-Memory-Handling================================================= */ + +static int lmsg_destructor(void *mem) +{ + ldap_msgfree((LDAPMessage *)mem); + return 0; +} + +static int sdap_msg_attach(TALLOC_CTX *memctx, LDAPMessage *msg) +{ + void *h; + + if (!msg) return EINVAL; + + h = sss_mem_attach(memctx, msg, lmsg_destructor); + if (!h) return ENOMEM; + + return EOK; +} + +/* ==sdap-hanlde-utility-functions======================================== */ + +static int sdap_handle_destructor(void *mem) +{ + struct sdap_handle *h = talloc_get_type(mem, struct sdap_handle); + if (h->connected) { + ldap_unbind_ext(h->ldap, NULL, NULL); + h->connected = false; + h->ldap = NULL; + h->fd = -1; + } + return 0; +} + +static struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx) +{ + struct sdap_handle *sh; + + sh = talloc_zero(memctx, struct sdap_handle); + if (!sh) return NULL; + + sh->fd = -1; + + talloc_set_destructor((TALLOC_CTX *)sh, sdap_handle_destructor); + + return sh; +} + +static int get_fd_from_ldap(LDAP *ldap, int *fd) +{ + int ret; + + ret = ldap_get_option(ldap, LDAP_OPT_DESC, fd); + if (ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to get fd from ldap!!\n")); + *fd = -1; + return EIO; + } + + return EOK; +} + +/* ==Parse-Results-And-Handle-Disconnections============================== */ + +static enum sdap_result sdap_check_result(struct sdap_handle *sh, + int msgid, bool wait_all, + LDAPMessage **msg, int *restype) +{ + struct timeval no_timeout = {0, 0}; + int ret; + + ret = ldap_result(sh->ldap, msgid, wait_all, &no_timeout, msg); + if (ret == 0) { + DEBUG(8, ("ldap result not ready yet (%d)\n", msgid)); + /* retry */ + return SDAP_RETRY; + } + if (ret == -1) { + DEBUG(2, ("ldap result not available (%d)\n", msgid)); + + /* Fatal error returned, kill the connection, and reset the handle */ + ldap_unbind_ext(sh->ldap, NULL, NULL); + sh->connected = false; + sh->ldap = NULL; + sh->fd = -1; + + return SDAP_ERROR; + } + DEBUG(8, ("ldap result returned %d\n", ret)); + + *restype = ret; + return SDAP_SUCCESS; +} + + +/* ==Connect-to-LDAP-Server=============================================== */ + +struct sdap_connect_state { + struct sdap_options *opts; + struct sdap_handle *sh; + + int msgid; + + struct sdap_msg *reply; + int result; +}; + +static void sdap_connect_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, 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 tevent_fd *fde; + struct timeval tv; + int ver; + int ret; + + 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->opts = opts; + state->sh = sdap_handle_create(state); + if (!state->sh) { + talloc_zfree(req); + return NULL; + } + /* Initialize LDAP handler */ + ret = ldap_initialize(&state->sh->ldap, opts->basic[SDAP_URI].value); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_initialize failed: %s\n", ldap_err2string(ret))); + goto fail; + } + + /* Force ldap version to 3 */ + ver = LDAP_VERSION3; + ret = ldap_set_option(state->sh->ldap, LDAP_OPT_PROTOCOL_VERSION, &ver); + if (ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set ldap version to 3\n")); + goto fail; + } + + /* Set Network Timeout */ + tv.tv_sec = opts->network_timeout; + tv.tv_usec = 0; + ret = ldap_set_option(state->sh->ldap, LDAP_OPT_NETWORK_TIMEOUT, &tv); + if (ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set network timeout to %d\n", + opts->network_timeout)); + goto fail; + } + + /* Set Default Timeout */ + tv.tv_sec = opts->opt_timeout; + tv.tv_usec = 0; + ret = ldap_set_option(state->sh->ldap, LDAP_OPT_TIMEOUT, &tv); + if (ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set default timeout to %d\n", + opts->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")); + + ret = ldap_start_tls(state->sh->ldap, NULL, NULL, &state->msgid); + if (ret != LDAP_SUCCESS) { + DEBUG(3, ("ldap_start_tls failed: [%s]", ldap_err2string(ret))); + goto fail; + } + + state->sh->connected = true; + ret = get_fd_from_ldap(state->sh->ldap, &state->sh->fd); + if (ret) goto fail; + + fde = tevent_add_fd(ev, state, + state->sh->fd, TEVENT_FD_READ, + sdap_connect_done, req); + if (!fde) { + DEBUG(1, ("Failed to set up fd event!\n")); + goto fail; + } + + return req; + +fail: + if (ret == LDAP_SERVER_DOWN) { + tevent_req_error(req, EAGAIN); + } else { + tevent_req_error(req, EIO); + } + tevent_req_post(req, ev); + return req; +} + +static void sdap_connect_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, 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); + enum sdap_result res; + char *errmsg; + int restype; + int ret; + + res = sdap_check_result(state->sh, state->msgid, true, + &state->reply->msg, &restype); + if (res != SDAP_SUCCESS) { + if (res != SDAP_RETRY) { + tevent_req_error(req, EIO); + } + return; + } + + ret = sdap_msg_attach(state->reply, state->reply->msg); + if (ret) { + DEBUG(1, ("Error appending memory: %s(%d)\n", strerror(ret), ret)); + } + + 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->msgid)); + tevent_req_error(req, EIO); + return; + } + + DEBUG(3, ("START TLS result: %s(%d), %s\n", + ldap_err2string(state->result), state->result, errmsg)); + +/* 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.\n")); + 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 asq 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 sdap_handle *sh; + const char *user_dn; + struct berval *pw; + int msgid; + + struct sdap_msg *reply; + int result; +}; + +static void simple_bind_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, 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; + struct tevent_fd *fde; + int ret; + + 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->sh = sh; + state->user_dn = user_dn; + state->pw = pw; + + 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, NULL, NULL, &state->msgid); + if (ret == -1 || state->msgid == -1) { + DEBUG(1, ("ldap_bind failed\n")); + goto fail; + } + DEBUG(8, ("ldap simple bind sent, msgid = %d\n", state->msgid)); + + if (!sh->connected) { + sh->connected = true; + ret = get_fd_from_ldap(sh->ldap, &sh->fd); + if (ret) goto fail; + } + + fde = tevent_add_fd(ev, state, + sh->fd, TEVENT_FD_READ, + simple_bind_done, req); + if (!fde) { + talloc_zfree(req); + } + return req; + +fail: + if (ret == LDAP_SERVER_DOWN) { + tevent_req_error(req, EAGAIN); + } else { + tevent_req_error(req, EIO); + } + tevent_req_post(req, ev); + return req; +} + +static void simple_bind_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, 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); + enum sdap_result res; + char *errmsg; + int restype; + int ret; + + res = sdap_check_result(state->sh, state->msgid, true, + &state->reply->msg, &restype); + if (res != SDAP_SUCCESS) { + if (res != SDAP_RETRY) { + tevent_req_error(req, EIO); + } + return; + } + + ret = sdap_msg_attach(state->reply, state->reply->msg); + if (ret) { + DEBUG(1, ("Error appending memory: %s(%d)\n", strerror(ret), ret)); + } + + 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->msgid)); + tevent_req_error(req, EIO); + return; + } + + DEBUG(3, ("Bind result: %s(%d), %s\n", + ldap_err2string(state->result), state->result, errmsg)); + + tevent_req_done(req); +} + +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; + return -1; + } + + *ldaperr = state->result; + return 0; +} + +/* ==Authenticaticate-User-by-DN========================================== */ + +struct sdap_auth_state { + const char *user_dn; + struct berval pw; + int msgid; + int result; +}; + +static void sdap_auth_done(struct tevent_req *subreq); + +struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_handle *sh, + const char *user_dn, + const char *password) +{ + struct tevent_req *req, *subreq; + struct sdap_auth_state *state; + + req = tevent_req_create(memctx, &state, struct sdap_auth_state); + if (!req) return NULL; + + state->user_dn = user_dn; + if (password) { + state->pw.bv_val = discard_const(password); + state->pw.bv_len = strlen(password); + } else { + state->pw.bv_val = NULL; + state->pw.bv_len = 0; + } + + subreq = simple_bind_send(state, ev, sh, user_dn, &state->pw); + if (!subreq) { + tevent_req_error(req, EFAULT); + 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; + + ret = simple_bind_recv(subreq, &state->result); + if (ret == -1) { + tevent_req_error(req, EFAULT); + 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; + default: + *result = SDAP_ERROR; + } + return EOK; +} + +/* ==Password=Caching===================================================== */ + +struct sdap_cache_pw_state { + struct sss_domain_info *domain; + const char *username; + const char *password; + + struct sysdb_req *sysreq; + + int result; +}; + +static void sdap_cache_pw_op(struct sysdb_req *req, void *pvt); +static void sdap_cache_pw_callback(void *pvt, int error, struct ldb_result *r); + +struct tevent_req *sdap_cache_pw_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *username, + const char *password) +{ + struct tevent_req *req; + struct sdap_cache_pw_state *state; + int ret; + + req = tevent_req_create(memctx, &state, struct sdap_cache_pw_state); + if (!req) return NULL; + + state->domain = domain; + state->username = username; + state->password = password; + + ret = sysdb_transaction(state, sysdb, sdap_cache_pw_op, req); + + if (ret != EOK) { + DEBUG(1, ("Failed to start sysydb transaction (%d)[%s]!?\n", + ret, strerror(ret))); + goto fail; + } + + return req; + +fail: + tevent_req_error(req, EIO); + tevent_req_post(req, ev); + return req; +} + +static void sdap_cache_pw_op(struct sysdb_req *sysreq, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_cache_pw_state *state = tevent_req_data(req, + struct sdap_cache_pw_state); + int ret; + + state->sysreq = sysreq; + + ret = sysdb_set_cached_password(sysreq, + state->domain, + state->username, + state->password, + sdap_cache_pw_callback, req); + if (ret != EOK) { + state->result = ret; + tevent_req_done(req); + } +} + +static void sdap_cache_pw_callback(void *pvt, int e, struct ldb_result *r) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_cache_pw_state *state = tevent_req_data(req, + struct sdap_cache_pw_state); + sysdb_transaction_done(state->sysreq, e); + + if (e != EOK) { + DEBUG(2, ("Failed to cache password (%d)[%s]!?\n", e, strerror(e))); + state->result = e; + } + + state->result = EOK; + tevent_req_done(req); +} + +int sdap_cache_pw_recv(struct tevent_req *req) +{ + struct sdap_cache_pw_state *state = tevent_req_data(req, + struct sdap_cache_pw_state); + enum tevent_req_state tstate; + uint64_t err; + + if (tevent_req_is_error(req, &tstate, &err)) { + return err; + } + return state->result; +} + +/* ==Save-User-Entry====================================================== */ + +struct sdap_save_user_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sdap_options *opts; + struct sdap_handle *sh; + + struct sss_domain_info *dom; + + struct sysdb_attrs *attrs; +}; + +static void sdap_save_user_done(struct tevent_req *subreq); + + /* FIXME: support non legacy */ + /* 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 sdap_handle *sh, + struct sdap_msg *entry) +{ + struct tevent_req *req, *subreq; + struct sdap_save_user_state *state; + struct ldb_message_element *el; + int ret; + const char *name; + const char *pwd; + const char *gecos; + const char *homedir; + const char *shell; + long int l; + uid_t uid; + gid_t gid; + + req = tevent_req_create(memctx, &state, struct sdap_save_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->sh = sh; + state->dom = dom; + state->opts = opts; + + ret = sdap_parse_user(state, state->opts, state->sh, + entry, &state->attrs, NULL); + if (ret) goto fail; + + 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; + } + 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; + 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; + errno = 0; + l = strtol((const char *)el->values[0].data, NULL, 0); + if (errno) { + ret = EINVAL; + goto fail; + } + gid = l; + + DEBUG(6, ("Storing info for user %s\n", name)); + + subreq = sysdb_store_user_send(state, state->ev, state->handle, + state->dom, name, pwd, uid, gid, + gecos, homedir, shell); + 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); + int ret; + + ret = sysdb_store_user_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sdap_save_user_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; +} + + +/* ==Save-Group-Entry===================================================== */ + +struct sdap_save_group_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sdap_options *opts; + struct sdap_handle *sh; + + struct sss_domain_info *dom; + + struct sysdb_attrs *attrs; +}; + +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 sdap_handle *sh, + struct sdap_msg *entry) +{ + struct tevent_req *req, *subreq; + struct sdap_save_group_state *state; + struct ldb_message_element *el; + int i, ret; + char *name; + const char **members; + long int l; + gid_t gid; + + req = tevent_req_create(memctx, &state, struct sdap_save_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->sh = sh; + state->dom = dom; + state->opts = opts; + + ret = sdap_parse_group(state, state->opts, state->sh, + entry, &state->attrs, NULL); + if (ret) goto fail; + + ret = sysdb_attrs_get_el(state->attrs, + opts->group_map[SDAP_AT_GROUP_NAME].sys_name, &el); + if (ret) goto fail; + if (el->num_values == 0) { + ret = EINVAL; + goto fail; + } + name = (char *)el->values[0].data; + + ret = sysdb_attrs_get_el(state->attrs, + opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el); + if (ret) goto fail; + if (el->num_values == 0) members = NULL; + else { + members = talloc_array(state, const char *, el->num_values +1); + if (!members) { + ret = ENOMEM; + goto fail; + } + for (i = 0; i < el->num_values; i++) { + members[i] = (char *)el->values[i].data; + } + members[i] = NULL; + } + + ret = sysdb_attrs_get_el(state->attrs, + opts->group_map[SDAP_AT_GROUP_GID].sys_name, &el); + if (ret) goto fail; + errno = 0; + l = strtol((const char *)el->values[0].data, NULL, 0); + if (errno) { + ret = EINVAL; + goto fail; + } + gid = l; + + DEBUG(6, ("Storing info for group %s\n", name)); + + subreq = sysdb_store_group_send(state, state->ev, state->handle, + state->dom, name, gid, members); + 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); + int ret; + + ret = sysdb_store_group_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sdap_save_group_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; +} + + +/* ==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_handle *handle; + + struct tevent_fd *fde; + int msgid; +}; + +static void sdap_get_users_transaction(struct tevent_req *subreq); +static void sdap_get_users_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt); +static void sdap_get_users_save_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; + int ret; + + 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; + + DEBUG(5, ("calling ldap_search_ext with [%s].\n", filter)); + + ret = ldap_search_ext(state->sh->ldap, + opts->basic[SDAP_USER_SEARCH_BASE].value, + LDAP_SCOPE_SUBTREE, filter, discard_const(attrs), + false, NULL, NULL, NULL, 0, &state->msgid); + if (ret != LDAP_SUCCESS) { + DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(ret))); + goto fail; + } + DEBUG(8, ("ldap_search_ext called, msgid = %d\n", state->msgid)); + + subreq = sysdb_transaction_send(state, state->ev, sysdb); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sdap_get_users_transaction, req); + + return req; + +fail: + tevent_req_error(req, EIO); + tevent_req_post(req, ev); + return req; +} + +static void sdap_get_users_transaction(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 = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + state->fde = tevent_add_fd(state->ev, state, + state->sh->fd, TEVENT_FD_READ, + sdap_get_users_done, req); + if (!state->fde) { + DEBUG(1, ("Failed to set up fd event!\n")); + tevent_req_error(req, ENOMEM); + } +} + +static void sdap_get_users_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_get_users_state *state = tevent_req_data(req, + struct sdap_get_users_state); + struct tevent_req *subreq; + LDAPMessage *msg = NULL; + struct sdap_msg *reply; + enum sdap_result res; + char *errmsg; + int restype; + int result; + int ret; + + res = sdap_check_result(state->sh, state->msgid, false, + &msg, &restype); + if (res != SDAP_SUCCESS) { + if (res != SDAP_RETRY) { + tevent_req_error(req, EIO); + return; + } + + /* make sure fd is readable so we can fetch the next result */ + TEVENT_FD_READABLE(state->fde); + return; + } + + if (!msg) { + tevent_req_error(req, EIO); + return; + } + + reply = talloc_zero(state, struct sdap_msg); + if (!reply) { + ldap_msgfree(msg); + tevent_req_error(req, ENOMEM); + return; + } + + reply->msg = msg; + ret = sdap_msg_attach(reply, msg); + if (ret) { + DEBUG(1, ("Error appending memory: %s(%d)\n", strerror(ret), ret)); + tevent_req_error(req, EFAULT); + return; + } + + switch (restype) { + case LDAP_RES_SEARCH_REFERENCE: + /* ignore references for now */ + ldap_msgfree(msg); + break; + + case LDAP_RES_SEARCH_ENTRY: + /* FIXME: should we set a timeout tevent timed function ? */ + + /* stop reading until operation is done */ + TEVENT_FD_NOT_READABLE(state->fde); + + subreq = sdap_save_user_send(state, state->ev, state->handle, + state->opts, state->dom, + state->sh, reply); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + /* attach reply to subreq, + * will not be needed anymore once subreq is done */ + talloc_steal(subreq, reply); + + tevent_req_set_callback(subreq, sdap_get_users_save_done, req); + break; + + case LDAP_RES_SEARCH_RESULT: + /* End of the story */ + + ret = ldap_parse_result(state->sh->ldap, reply->msg, + &result, NULL, &errmsg, NULL, NULL, 0); + if (ret != LDAP_SUCCESS) { + DEBUG(2, ("ldap_parse_result failed (%d)\n", state->msgid)); + tevent_req_error(req, EIO); + return; + } + + DEBUG(3, ("Search result: %s(%d), %s\n", + ldap_err2string(result), result, errmsg)); + + 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); + break; + + default: + /* what is going on here !? */ + tevent_req_error(req, EIO); + return; + } +} + +static void sdap_fake_users_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt); + +static void sdap_get_users_save_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); + struct timeval tv = { 0, 0 }; + struct tevent_timer *te; + int ret; + + ret = sdap_save_user_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + /* unfortunately LDAP libraries consume everything sitting on the wire but + * do not give us a way to know if there is anything waiting to be read or + * or not. So schedule a fake fde event and wake up ourselves again. If we + * get a SDAP_RETRY it is fine. */ + + te = tevent_add_timer(state->ev, state, tv, + sdap_fake_users_done, req); + if (!te) { + tevent_req_error(req, ENOMEM); + return; + } +} + +static void sdap_fake_users_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_get_users_state *state = tevent_req_data(req, + struct sdap_get_users_state); + + sdap_get_users_done(state->ev, state->fde, 0, pvt); +} + + +int sdap_get_users_recv(struct tevent_req *req) +{ + 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)) { + + /* FIXME: send abandon ? + * read all to flush the read queue ? + * close the connection ? */ + + /* closing for now */ + ldap_unbind_ext(state->sh->ldap, NULL, NULL); + state->sh->connected = false; + state->sh->ldap = NULL; + state->sh->fd = -1; + + return err; + } + + 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_handle *handle; + + struct tevent_fd *fde; + int msgid; +}; + +static void sdap_get_groups_transaction(struct tevent_req *subreq); +static void sdap_get_groups_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt); +static void sdap_get_groups_save_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; + int ret; + + 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; + + DEBUG(5, ("calling ldap_search_ext with [%s].\n", filter)); + + ret = ldap_search_ext(state->sh->ldap, + opts->basic[SDAP_GROUP_SEARCH_BASE].value, + LDAP_SCOPE_SUBTREE, filter, discard_const(attrs), + false, NULL, NULL, NULL, 0, &state->msgid); + if (ret != LDAP_SUCCESS) { + DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(ret))); + goto fail; + } + DEBUG(8, ("ldap_search_ext called, msgid = %d\n", state->msgid)); + + subreq = sysdb_transaction_send(state, state->ev, sysdb); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sdap_get_groups_transaction, req); + + return req; + +fail: + tevent_req_error(req, EIO); + tevent_req_post(req, ev); + return req; +} + +static void sdap_get_groups_transaction(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 = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + state->fde = tevent_add_fd(state->ev, state, + state->sh->fd, TEVENT_FD_READ, + sdap_get_groups_done, req); + if (!state->fde) { + DEBUG(1, ("Failed to set up fd event!\n")); + tevent_req_error(req, ENOMEM); + } +} + +static void sdap_get_groups_done(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_get_groups_state *state = tevent_req_data(req, + struct sdap_get_groups_state); + struct tevent_req *subreq; + LDAPMessage *msg = NULL; + struct sdap_msg *reply; + enum sdap_result res; + char *errmsg; + int restype; + int result; + int ret; + + res = sdap_check_result(state->sh, state->msgid, false, + &msg, &restype); + if (res != SDAP_SUCCESS) { + if (res != SDAP_RETRY) { + tevent_req_error(req, EIO); + return; + } + + /* make sure fd is readable so we can fetch the next result */ + TEVENT_FD_READABLE(state->fde); + return; + } + + if (!msg) { + tevent_req_error(req, EIO); + return; + } + + reply = talloc_zero(state, struct sdap_msg); + if (!reply) { + ldap_msgfree(msg); + tevent_req_error(req, ENOMEM); + return; + } + + reply->msg = msg; + ret = sdap_msg_attach(reply, msg); + if (ret) { + DEBUG(1, ("Error appending memory: %s(%d)\n", strerror(ret), ret)); + tevent_req_error(req, EFAULT); + return; + } + + switch (restype) { + case LDAP_RES_SEARCH_REFERENCE: + /* ignore references for now */ + ldap_msgfree(msg); + break; + + case LDAP_RES_SEARCH_ENTRY: + /* FIXME: should we set a timeout tevent timed function ? */ + + /* stop reading until operation is done */ + TEVENT_FD_NOT_READABLE(state->fde); + + subreq = sdap_save_group_send(state, state->ev, state->handle, + state->opts, state->dom, + state->sh, reply); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + /* attach reply to subreq, + * will not be needed anymore once subreq is done */ + talloc_steal(subreq, reply); + + tevent_req_set_callback(subreq, sdap_get_groups_save_done, req); + break; + + case LDAP_RES_SEARCH_RESULT: + /* End of the story */ + + ret = ldap_parse_result(state->sh->ldap, reply->msg, + &result, NULL, &errmsg, NULL, NULL, 0); + if (ret != LDAP_SUCCESS) { + DEBUG(2, ("ldap_parse_result failed (%d)\n", state->msgid)); + tevent_req_error(req, EIO); + return; + } + + DEBUG(3, ("Search result: %s(%d), %s\n", + ldap_err2string(result), result, errmsg)); + + 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); + break; + + default: + /* what is going on here !? */ + tevent_req_error(req, EIO); + return; + } +} + +static void sdap_fake_groups_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt); + +static void sdap_get_groups_save_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); + struct timeval tv = { 0, 0 }; + struct tevent_timer *te; + int ret; + + ret = sdap_save_group_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + /* unfortunately LDAP libraries consume everything sitting on the wire but + * do not give us a way to know if there is anything waiting to be read or + * or not. So schedule a fake fde event and wake up ourselves again. If we + * get a SDAP_RETRY it is fine. */ + + te = tevent_add_timer(state->ev, state, tv, + sdap_fake_groups_done, req); + if (!te) { + tevent_req_error(req, ENOMEM); + return; + } +} + +static void sdap_fake_groups_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_get_groups_state *state = tevent_req_data(req, + struct sdap_get_groups_state); + + sdap_get_groups_done(state->ev, state->fde, 0, pvt); +} + + +int sdap_get_groups_recv(struct tevent_req *req) +{ + 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)) { + + /* FIXME: send abandon ? + * read all to flush the read queue ? + * close the connection ? */ + + /* closing for now */ + ldap_unbind_ext(state->sh->ldap, NULL, NULL); + state->sh->connected = false; + state->sh->ldap = NULL; + state->sh->fd = -1; + + return err; + } + + return EOK; +} + diff --git a/server/providers/ldap/sdap_async.h b/server/providers/ldap/sdap_async.h new file mode 100644 index 00000000..6b72ac04 --- /dev/null +++ b/server/providers/ldap/sdap_async.h @@ -0,0 +1,90 @@ +/* + 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/>. +*/ + +#include <talloc.h> +#include <tevent.h> +#include "providers/dp_backend.h" +#include "providers/ldap/sdap.h" + +/* TODO: remove later + * These functions are available in the latest tevent and are the ones that + * should be used as tevent_req is rightfully opaque there */ +#ifndef tevent_req_data +#define tevent_req_data(req, type) ((type *)req->private_state) +#endif + +#ifndef tevent_req_set_callback +#define tevent_req_set_callback(req, func, data) \ + do { req->async.fn = func; req->async.private_data = data; } while(0) +#endif + +#ifndef tevent_req_callback_data +#define tevent_req_callback_data(req, type) ((type *)req->async.private_data) +#endif + + +struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_options *opts, + bool use_start_tls); + +int sdap_connect_recv(struct tevent_req *req, + TALLOC_CTX *memctx, + struct sdap_handle **sh); + +struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sss_domain_info *dom, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sdap_handle *sh, + const char **attrs, + const char *wildcard); + +int sdap_get_users_recv(struct tevent_req *req); + +struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sss_domain_info *dom, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sdap_handle *sh, + const char **attrs, + const char *wildcard); + +int sdap_get_groups_recv(struct tevent_req *req); + +struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_handle *sh, + const char *user_dn, + const char *password); + +int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result); + +struct tevent_req *sdap_cache_pw_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *username, + const char *password); + +int sdap_cache_pw_recv(struct tevent_req *req); |