From 796463906a54e259bd5b582ce84af4297a58eafc Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Sat, 28 Jan 2012 11:40:00 -0500 Subject: LDAP: Add support for service lookups (non-enum) --- src/providers/ldap/ldap_common.c | 33 +++ src/providers/ldap/ldap_common.h | 12 + src/providers/ldap/ldap_id.c | 30 ++ src/providers/ldap/ldap_id_services.c | 290 ++++++++++++++++++ src/providers/ldap/sdap.c | 11 + src/providers/ldap/sdap.h | 13 + src/providers/ldap/sdap_async.h | 17 ++ src/providers/ldap/sdap_async_services.c | 485 +++++++++++++++++++++++++++++++ 8 files changed, 891 insertions(+) create mode 100644 src/providers/ldap/ldap_id_services.c create mode 100644 src/providers/ldap/sdap_async_services.c (limited to 'src/providers') diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 3d7f9430..786e06b3 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -28,6 +28,7 @@ #include "providers/krb5/krb5_common.h" #include "providers/ldap/sdap_sudo_timer.h" #include "db/sysdb_sudo.h" +#include "db/sysdb_services.h" #include "util/sss_krb5.h" #include "util/crypto/sss_crypto.h" @@ -51,6 +52,7 @@ struct dp_option default_basic_opts[] = { { "ldap_group_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_group_search_scope", DP_OPT_STRING, { "sub" }, NULL_STRING }, { "ldap_group_search_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "ldap_service_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_sudo_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_sudo_refresh_enabled", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "ldap_sudo_refresh_timeout", DP_OPT_NUMBER, { .number = 300 }, NULL_NUMBER }, @@ -234,6 +236,14 @@ struct sdap_attr_map native_sudorule_map[] = { { "ldap_sudorule_order", "sudoOrder", SYSDB_SUDO_CACHE_AT_ORDER, NULL } }; +struct sdap_attr_map service_map[] = { + { "ldap_service_object_class", "ipService", SYSDB_SVC_CLASS, NULL }, + { "ldap_service_name", "cn", SYSDB_NAME, NULL }, + { "ldap_service_port", "ipServicePort", SYSDB_SVC_PORT, NULL }, + { "ldap_service_proto", "ipServiceProtocol", SYSDB_SVC_PROTO, NULL }, + { "ldap_service_entry_usn", NULL, SYSDB_USN, NULL } +}; + int ldap_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb, const char *conf_path, @@ -243,6 +253,7 @@ int ldap_get_options(TALLOC_CTX *memctx, struct sdap_attr_map *default_user_map; struct sdap_attr_map *default_group_map; struct sdap_attr_map *default_netgroup_map; + struct sdap_attr_map *default_service_map; struct sdap_options *opts; char *schema; const char *search_base; @@ -259,6 +270,7 @@ int ldap_get_options(TALLOC_CTX *memctx, const int search_base_options[] = { SDAP_USER_SEARCH_BASE, SDAP_GROUP_SEARCH_BASE, SDAP_NETGROUP_SEARCH_BASE, + SDAP_SERVICE_SEARCH_BASE, -1 }; opts = talloc_zero(memctx, struct sdap_options); @@ -318,6 +330,12 @@ int ldap_get_options(TALLOC_CTX *memctx, &opts->netgroup_search_bases); if (ret != EOK && ret != ENOENT) goto done; + /* Service search */ + ret = sdap_parse_search_base(opts, opts->basic, + SDAP_SERVICE_SEARCH_BASE, + &opts->service_search_bases); + if (ret != EOK && ret != ENOENT) goto done; + pwd_policy = dp_opt_get_string(opts->basic, SDAP_PWD_POLICY); if (pwd_policy == NULL) { DEBUG(1, ("Missing password policy, this may not happen.\n")); @@ -402,6 +420,7 @@ int ldap_get_options(TALLOC_CTX *memctx, default_user_map = rfc2307_user_map; default_group_map = rfc2307_group_map; default_netgroup_map = netgroup_map; + default_service_map = service_map; } else if (strcasecmp(schema, "rfc2307bis") == 0) { opts->schema_type = SDAP_SCHEMA_RFC2307BIS; @@ -409,6 +428,7 @@ int ldap_get_options(TALLOC_CTX *memctx, default_user_map = rfc2307bis_user_map; default_group_map = rfc2307bis_group_map; default_netgroup_map = netgroup_map; + default_service_map = service_map; } else if (strcasecmp(schema, "IPA") == 0) { opts->schema_type = SDAP_SCHEMA_IPA_V1; @@ -416,6 +436,7 @@ int ldap_get_options(TALLOC_CTX *memctx, default_user_map = rfc2307bis_user_map; default_group_map = rfc2307bis_group_map; default_netgroup_map = netgroup_map; + default_service_map = service_map; } else if (strcasecmp(schema, "AD") == 0) { opts->schema_type = SDAP_SCHEMA_AD; @@ -423,6 +444,7 @@ int ldap_get_options(TALLOC_CTX *memctx, default_user_map = rfc2307bis_user_map; default_group_map = rfc2307bis_group_map; default_netgroup_map = netgroup_map; + default_service_map = service_map; } else { DEBUG(0, ("Unrecognized schema type: %s\n", schema)); ret = EINVAL; @@ -461,6 +483,14 @@ int ldap_get_options(TALLOC_CTX *memctx, goto done; } + ret = sdap_get_map(opts, cdb, conf_path, + default_service_map, + SDAP_OPTS_SERVICES, + &opts->service_map); + if (ret != EOK) { + goto done; + } + /* If there is no KDC, try the deprecated krb5_kdcip option, too */ /* FIXME - this can be removed in a future version */ ret = krb5_try_kdcip(memctx, cdb, conf_path, opts->basic, SDAP_KRB5_KDC); @@ -665,6 +695,9 @@ errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx, case SDAP_SUDO_SEARCH_BASE: class_name = "SUDO"; break; + case SDAP_SERVICE_SEARCH_BASE: + class_name = "SERVICE"; + break; default: DEBUG(SSSDBG_CONF_SETTINGS, ("Unknown search base type: [%d]\n", class)); diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index 0ab0a5e1..cda21da4 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -155,6 +155,18 @@ struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx, const char *name); int ldap_netgroup_get_recv(struct tevent_req *req, int *dp_error_out); + +struct tevent_req * +services_get_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_id_ctx *id_ctx, + const char *name, + const char *protocol, + int filter_type); + +errno_t +services_get_recv(struct tevent_req *req, int *dp_error_out); + /* setup child logging */ int setup_child(struct sdap_id_ctx *ctx); diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c index feac63b6..db49e77d 100644 --- a/src/providers/ldap/ldap_id.c +++ b/src/providers/ldap/ldap_id.c @@ -756,6 +756,7 @@ static void sdap_account_info_users_done(struct tevent_req *req); static void sdap_account_info_groups_done(struct tevent_req *req); static void sdap_account_info_initgr_done(struct tevent_req *req); static void sdap_account_info_netgroups_done(struct tevent_req *req); +static void sdap_account_info_services_done(struct tevent_req *req); void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx); void sdap_account_info_handler(struct be_req *breq) @@ -858,6 +859,24 @@ void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx) tevent_req_set_callback(req, sdap_account_info_netgroups_done, breq); break; + case BE_REQ_SERVICES: + /* skip enumerations on demand */ + if (ar->filter_type == BE_FILTER_ENUM) { + return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); + } + + req = services_get_send(breq, breq->be_ctx->ev, ctx, + ar->filter_value, + ar->extra_value, + ar->filter_type); + if (!req) { + return sdap_handler_done(breq, DP_ERR_FATAL, + ENOMEM, "Out of memory"); + } + tevent_req_set_callback(req, sdap_account_info_services_done, breq); + + break; + default: /*fail*/ ret = EINVAL; err = "Invalid request type"; @@ -933,3 +952,14 @@ static void sdap_account_info_netgroups_done(struct tevent_req *req) sdap_account_info_complete(breq, dp_error, ret, "Netgroup lookup failed"); } + +static void sdap_account_info_services_done(struct tevent_req *req) +{ + struct be_req *breq = tevent_req_callback_data(req, struct be_req); + int ret, dp_error; + + ret = services_get_recv(req, &dp_error); + talloc_zfree(req); + + sdap_account_info_complete(breq, dp_error, ret, "Service lookup failed"); +} diff --git a/src/providers/ldap/ldap_id_services.c b/src/providers/ldap/ldap_id_services.c new file mode 100644 index 00000000..bd7bb562 --- /dev/null +++ b/src/providers/ldap/ldap_id_services.c @@ -0,0 +1,290 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include + +#include "util/util.h" +#include "util/strtonum.h" +#include "db/sysdb.h" +#include "db/sysdb_services.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap_async.h" + +struct sdap_services_get_state { + struct tevent_context *ev; + struct sdap_id_ctx *id_ctx; + struct sdap_id_op *op; + struct sysdb_ctx *sysdb; + struct sss_domain_info *domain; + + const char *name; + const char *protocol; + + char *filter; + const char **attrs; + + int filter_type; + + int dp_error; +}; + +static errno_t +services_get_retry(struct tevent_req *req); +static void +services_get_connect_done(struct tevent_req *subreq); +static void +services_get_done(struct tevent_req *subreq); + +struct tevent_req * +services_get_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_id_ctx *id_ctx, + const char *name, + const char *protocol, + int filter_type) +{ + errno_t ret; + struct tevent_req *req; + struct sdap_services_get_state *state; + const char *attr_name; + char *clean_name; + char *clean_protocol = NULL; + + req = tevent_req_create(mem_ctx, &state, struct sdap_services_get_state); + if (!req) return NULL; + + state->ev = ev; + state->id_ctx = id_ctx; + state->dp_error = DP_ERR_FATAL; + state->sysdb = id_ctx->be->sysdb; + state->domain = state->id_ctx->be->domain; + state->name = name; + state->filter_type = filter_type; + + state->op = sdap_id_op_create(state, state->id_ctx->conn_cache); + if (!state->op) { + DEBUG(SSSDBG_MINOR_FAILURE, ("sdap_id_op_create failed\n")); + ret = ENOMEM; + goto error; + } + + switch(filter_type) { + case BE_FILTER_NAME: + attr_name = id_ctx->opts->service_map[SDAP_AT_SERVICE_NAME].name; + break; + case BE_FILTER_IDNUM: + attr_name = id_ctx->opts->service_map[SDAP_AT_SERVICE_PORT].name; + break; + default: + ret = EINVAL; + goto error; + } + + ret = sss_filter_sanitize(state, name, &clean_name); + if (ret != EOK) goto error; + + if (protocol) { + ret = sss_filter_sanitize(state, protocol, &clean_protocol); + if (ret != EOK) goto error; + } + + if (clean_protocol) { + state->filter = talloc_asprintf( + state, "(&(%s=%s)(%s=%s)(objectclass=%s))", + attr_name, clean_name, + id_ctx->opts->service_map[SDAP_AT_SERVICE_PROTOCOL].name, + clean_protocol, + id_ctx->opts->service_map[SDAP_OC_SERVICE].name); + } else { + state->filter = + talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + attr_name, clean_name, + id_ctx->opts->service_map[SDAP_OC_SERVICE].name); + } + talloc_zfree(clean_name); + talloc_zfree(clean_protocol); + if (!state->filter) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to build the base filter\n")); + ret = ENOMEM; + goto error; + } + DEBUG(SSSDBG_TRACE_LIBS, + ("Preparing to search for services with filter [%s]\n", + state->filter)); + + ret = build_attrs_from_map(state, id_ctx->opts->service_map, + SDAP_OPTS_SERVICES, &state->attrs); + if (ret != EOK) goto error; + + ret = services_get_retry(req); + if (ret != EOK) goto error; + + return req; + +error: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static errno_t +services_get_retry(struct tevent_req *req) +{ + errno_t ret; + struct sdap_services_get_state *state = + tevent_req_data(req, struct sdap_services_get_state); + struct tevent_req *subreq; + + subreq = sdap_id_op_connect_send(state->op, state, &ret); + if (!subreq) { + return ret; + } + + tevent_req_set_callback(subreq, services_get_connect_done, req); + return EOK; +} + +static void +services_get_connect_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct sdap_services_get_state *state = + tevent_req_data(req, struct sdap_services_get_state); + int dp_error = DP_ERR_FATAL; + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + + if (ret != EOK) { + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + subreq = sdap_get_services_send(state, state->ev, + state->domain, state->sysdb, + state->id_ctx->opts, + state->id_ctx->opts->service_search_bases, + sdap_id_op_handle(state->op), + state->attrs, state->filter, + dp_opt_get_int(state->id_ctx->opts->basic, + SDAP_SEARCH_TIMEOUT), + false); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, services_get_done, req); +} + +static void +services_get_done(struct tevent_req *subreq) +{ + errno_t ret; + uint16_t port; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct sdap_services_get_state *state = + tevent_req_data(req, struct sdap_services_get_state); + int dp_error = DP_ERR_FATAL; + + ret = sdap_get_services_recv(NULL, subreq, NULL); + talloc_zfree(subreq); + + /* Check whether we need to try again with another + * failover server. + */ + ret = sdap_id_op_done(state->op, ret, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = services_get_retry(req); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + /* Return to the mainloop to retry */ + return; + } + + /* An error occurred. */ + if (ret && ret != ENOENT) { + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + if (ret == ENOENT) { + /* Ensure that this entry is removed from the sysdb */ + switch(state->filter_type) { + case BE_FILTER_NAME: + ret = sysdb_svc_delete(state->sysdb, state->name, + 0, state->protocol); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + break; + + case BE_FILTER_IDNUM: + port = strtouint16(state->name, NULL, 10); + if (errno) { + tevent_req_error(req, errno); + return; + } + + ret = sysdb_svc_delete(state->sysdb, NULL, + port, state->protocol); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + break; + + default: + tevent_req_error(req, EINVAL); + return; + } + } + + state->dp_error = DP_ERR_OK; + tevent_req_done(req); +} + +errno_t +services_get_recv(struct tevent_req *req, int *dp_error_out) +{ + struct sdap_services_get_state *state = + tevent_req_data(req, struct sdap_services_get_state); + + if (dp_error_out) { + *dp_error_out = state->dp_error; + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c index c366dda0..ba1c7911 100644 --- a/src/providers/ldap/sdap.c +++ b/src/providers/ldap/sdap.c @@ -715,6 +715,9 @@ static errno_t sdap_set_search_base(struct sdap_options *opts, case SDAP_SUDO_SEARCH_BASE: bases = &opts->sudo_search_bases; break; + case SDAP_SERVICE_SEARCH_BASE: + bases = &opts->service_search_bases; + break; default: return EINVAL; } @@ -796,6 +799,14 @@ errno_t sdap_set_config_options_with_rootdse(struct sysdb_attrs *rootdse, if (ret != EOK) goto done; } + /* Services */ + if (!opts->service_search_bases) { + ret = sdap_set_search_base(opts, + SDAP_SERVICE_SEARCH_BASE, + naming_context); + if (ret != EOK) goto done; + } + ret = EOK; done: diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index 1827d7c6..bbc59414 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -164,6 +164,7 @@ enum sdap_basic_opt { SDAP_GROUP_SEARCH_BASE, SDAP_GROUP_SEARCH_SCOPE, SDAP_GROUP_SEARCH_FILTER, + SDAP_SERVICE_SEARCH_BASE, SDAP_SUDO_SEARCH_BASE, SDAP_SUDO_REFRESH_ENABLED, SDAP_SUDO_REFRESH_TIMEOUT, @@ -302,6 +303,15 @@ enum sdap_sudorule_attrs { SDAP_OPTS_SUDO /* attrs counter */ }; +enum sdap_service_attrs { + SDAP_OC_SERVICE = 0, + SDAP_AT_SERVICE_NAME, + SDAP_AT_SERVICE_PORT, + SDAP_AT_SERVICE_PROTOCOL, + SDAP_AT_SERVICE_USN, + SDAP_OPTS_SERVICES /* attrs counter */ +}; + struct sdap_attr_map { const char *opt_name; const char *def_name; @@ -322,6 +332,8 @@ struct sdap_options { struct sdap_attr_map *group_map; struct sdap_attr_map *netgroup_map; struct sdap_attr_map *host_map; + struct sdap_attr_map *service_map; + /* FIXME - should this go to a special struct to avoid mixing with name-service-switch maps? */ struct sdap_attr_map *sudorule_map; @@ -338,6 +350,7 @@ struct sdap_options { struct sdap_search_base **group_search_bases; struct sdap_search_base **netgroup_search_bases; struct sdap_search_base **sudo_search_bases; + struct sdap_search_base **service_search_bases; }; struct sdap_server_opts { diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index 2fd606bc..abf16b0c 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -211,4 +211,21 @@ errno_t sdap_save_all_names(const char *name, bool lowercase, struct sysdb_attrs *attrs); +struct tevent_req * +sdap_get_services_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sss_domain_info *dom, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sdap_search_base **search_bases, + struct sdap_handle *sh, + const char **attrs, + const char *filter, + int timeout, + bool enumeration); +errno_t +sdap_get_services_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + char **usn_value); + #endif /* _SDAP_ASYNC_H_ */ diff --git a/src/providers/ldap/sdap_async_services.c b/src/providers/ldap/sdap_async_services.c new file mode 100644 index 00000000..6fab4ace --- /dev/null +++ b/src/providers/ldap/sdap_async_services.c @@ -0,0 +1,485 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "db/sysdb.h" +#include "db/sysdb_services.h" +#include "providers/ldap/sdap_async_private.h" +#include "providers/ldap/ldap_common.h" + +struct sdap_get_services_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 *base_filter; + char *filter; + int timeout; + bool enumeration; + + char *higher_usn; + struct sysdb_attrs **services; + size_t count; + + size_t base_iter; + struct sdap_search_base **search_bases; +}; + +static errno_t +sdap_get_services_next_base(struct tevent_req *req); +static void +sdap_get_services_process(struct tevent_req *subreq); +static errno_t +sdap_save_services(TALLOC_CTX *memctx, + struct sysdb_ctx *sysdb, + const char **attrs, + struct sss_domain_info *dom, + struct sdap_options *opts, + struct sysdb_attrs **services, + size_t num_services, + char **_usn_value); +static errno_t +sdap_save_service(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sss_domain_info *dom, + struct sysdb_attrs *attrs, + const char **ldap_attrs, + char **_usn_value, + time_t now); + +struct tevent_req * +sdap_get_services_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sss_domain_info *dom, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sdap_search_base **search_bases, + struct sdap_handle *sh, + const char **attrs, + const char *filter, + int timeout, + bool enumeration) +{ + errno_t ret; + struct tevent_req *req; + struct sdap_get_services_state *state; + + req = tevent_req_create(memctx, &state, struct sdap_get_services_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->dom = dom; + state->sh = sh; + state->sysdb = sysdb; + state->attrs = attrs; + state->higher_usn = NULL; + state->services = NULL; + state->count = 0; + state->timeout = timeout; + state->base_filter = filter; + state->base_iter = 0; + state->search_bases = search_bases; + state->enumeration = enumeration; + + ret = sdap_get_services_next_base(req); + if (ret != EOK) { + tevent_req_error(req, ret); + tevent_req_post(req, state->ev); + } + + return req; +} + +static errno_t +sdap_get_services_next_base(struct tevent_req *req) +{ + struct tevent_req *subreq; + struct sdap_get_services_state *state; + + state = tevent_req_data(req, struct sdap_get_services_state); + + talloc_zfree(state->filter); + state->filter = sdap_get_id_specific_filter(state, + state->base_filter, + state->search_bases[state->base_iter]->filter); + if (!state->filter) { + return ENOMEM; + } + + DEBUG(SSSDBG_TRACE_FUNC, + ("Searching for services with base [%s]\n", + state->search_bases[state->base_iter]->basedn)); + + subreq = sdap_get_generic_send( + state, state->ev, state->opts, state->sh, + state->search_bases[state->base_iter]->basedn, + state->search_bases[state->base_iter]->scope, + state->filter, state->attrs, + state->opts->service_map, SDAP_OPTS_SERVICES, + state->timeout); + if (!subreq) { + return ENOMEM; + } + tevent_req_set_callback(subreq, sdap_get_services_process, req); + + return EOK; +} + +static void +sdap_get_services_process(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct sdap_get_services_state *state = + tevent_req_data(req, struct sdap_get_services_state); + int ret; + size_t count, i; + struct sysdb_attrs **services; + bool next_base = false; + + ret = sdap_get_generic_recv(subreq, state, + &count, &services); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, + ("Search for services, returned %d results.\n", + count)); + + if (state->enumeration || count == 0) { + /* No services found in this search or enumerating */ + next_base = true; + } + + /* Add this batch of sevices to the list */ + if (count > 0) { + state->services = + talloc_realloc(state, + state->services, + struct sysdb_attrs *, + state->count + count + 1); + if (!state->services) { + tevent_req_error(req, ENOMEM); + return; + } + + /* Copy the new services into the list + * They're already allocated on 'state' + */ + for (i = 0; i < count; i++) { + state->services[state->count + i] = services[i]; + } + + state->count += count; + state->services[state->count] = NULL; + } + + if (next_base) { + state->base_iter++; + if (state->search_bases[state->base_iter]) { + /* There are more search bases to try */ + ret = sdap_get_services_next_base(req); + if (ret != EOK) { + tevent_req_error(req, ret); + } + return; + } + } + + /* No more search bases + * Return ENOENT if no services were found + */ + if (state->count == 0) { + tevent_req_error(req, ENOENT); + return; + } + + ret = sdap_save_services(state, state->sysdb, + state->attrs, + state->dom, state->opts, + state->services, state->count, + &state->higher_usn); + if (ret) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to store services.\n")); + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Saving %d services - Done\n", state->count)); + + tevent_req_done(req); +} + +static errno_t +sdap_save_services(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char **attrs, + struct sss_domain_info *dom, + struct sdap_options *opts, + struct sysdb_attrs **services, + size_t num_services, + char **_usn_value) +{ + errno_t ret, sret; + time_t now; + size_t i; + bool in_transaction; + char *higher_usn = NULL; + char *usn_value; + TALLOC_CTX *tmp_ctx; + + if (num_services == 0) { + /* Nothing to do */ + return ENOENT; + } + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) goto done; + + in_transaction = true; + + now = time(NULL); + for (i = 0; i < num_services; i++) { + usn_value = NULL; + + ret = sdap_save_service(tmp_ctx, sysdb, opts, dom, + services[i], attrs, + &usn_value, now); + + /* Do not fail completely on errors. + * Just report the failure to save and go on */ + if (ret) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to store service %d. Ignoring.\n", i)); + } else { + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Service [%lu/%lu] processed!\n", i, num_services)); + } + + if (usn_value) { + if (higher_usn) { + if ((strlen(usn_value) > strlen(higher_usn)) || + (strcmp(usn_value, higher_usn) > 0)) { + talloc_zfree(higher_usn); + higher_usn = usn_value; + } else { + talloc_zfree(usn_value); + } + } else { + higher_usn = usn_value; + } + } + } + + ret = sysdb_transaction_commit(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to commit transaction!\n")); + goto done; + } + in_transaction = false; + + if (_usn_value) { + *_usn_value = talloc_steal(mem_ctx, higher_usn); + } + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to cancel transaction!\n")); + } + } + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +sdap_save_service(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sdap_options *opts, + struct sss_domain_info *dom, + struct sysdb_attrs *attrs, + const char **ldap_attrs, + char **_usn_value, + time_t now) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx = NULL; + struct sysdb_attrs *svc_attrs; + struct ldb_message_element *el; + char *usn_value = NULL; + const char *name = NULL; + const char **aliases; + const char **protocols; + char **missing; + uint16_t port; + uint64_t cache_timeout; + + DEBUG(SSSDBG_TRACE_ALL, ("Saving service\n")); + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + ret = ENOMEM; + goto done; + } + + svc_attrs = sysdb_new_attrs(tmp_ctx); + if (!svc_attrs) { + ret = ENOMEM; + goto done; + } + + /* Identify the primary name of this services */ + ret = sysdb_attrs_primary_name( + sysdb, attrs, + opts->service_map[SDAP_AT_SERVICE_NAME].name, + &name); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not determine the primary name of the service\n")); + goto done; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, ("Primary name: [%s]\n", name)); + + + /* Handle any available aliases */ + ret = sysdb_attrs_get_aliases(tmp_ctx, attrs, name, + !dom->case_sensitive, + &aliases); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to identify service aliases\n")); + goto done; + } + + /* Get the port number */ + ret = sysdb_attrs_get_uint16_t(attrs, SYSDB_SVC_PORT, &port); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to identify service port: [%s]\n", + strerror(ret))); + goto done; + } + + /* Get the protocols this service offers on that port */ + ret = sysdb_attrs_get_string_array(attrs, SYSDB_SVC_PROTO, + tmp_ctx, &protocols); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to identify service protocols: [%s]\n", + strerror(ret))); + goto done; + } + + /* Get the USN value, if available */ + ret = sysdb_attrs_get_el(attrs, + opts->service_map[SDAP_AT_SERVICE_USN].sys_name, &el); + if (ret && ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to retrieve USN value: [%s]\n", + strerror(ret))); + goto done; + } + if (ret == ENOENT || el->num_values == 0) { + DEBUG(SSSDBG_TRACE_LIBS, + ("Original USN value is not available for [%s].\n", + name)); + } else { + ret = sysdb_attrs_add_string(svc_attrs, + opts->service_map[SDAP_AT_SERVICE_USN].sys_name, + (const char*)el->values[0].data); + if (ret) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to add USN value: [%s]\n", + strerror(ret))); + goto done; + } + usn_value = talloc_strdup(tmp_ctx, (const char*)el->values[0].data); + if (!usn_value) { + ret = ENOMEM; + goto done; + } + } + + /* Make sure to remove any extra attributes from the sysdb + * that have been removed from LDAP + */ + ret = list_missing_attrs(svc_attrs, opts->service_map, SDAP_OPTS_SERVICES, + ldap_attrs, attrs, &missing); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to identify removed attributes: [%s]\n", + strerror(ret))); + goto done; + } + + cache_timeout = dp_opt_get_int(opts->basic, SDAP_ENTRY_CACHE_TIMEOUT); + + ret = sysdb_store_service(sysdb, name, port, aliases, protocols, + svc_attrs, missing, cache_timeout, now); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Failed to store service in the sysdb: [%s]\n", + strerror(ret))); + goto done; + } + + *_usn_value = talloc_steal(mem_ctx, usn_value); + +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t +sdap_get_services_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + char **usn_value) +{ + struct sdap_get_services_state *state = + tevent_req_data(req, struct sdap_get_services_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (usn_value) { + *usn_value = talloc_steal(mem_ctx, state->higher_usn); + } + + return EOK; +} -- cgit