summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2012-01-28 11:40:00 -0500
committerStephen Gallagher <sgallagh@redhat.com>2012-01-31 09:37:41 -0500
commit796463906a54e259bd5b582ce84af4297a58eafc (patch)
treeee032e15dd5d0b4bfd08d6db212e938d054cd9f0 /src
parent48b6eab1b369107af0d568e016a87637b7affc55 (diff)
downloadsssd-796463906a54e259bd5b582ce84af4297a58eafc.tar.gz
sssd-796463906a54e259bd5b582ce84af4297a58eafc.tar.xz
sssd-796463906a54e259bd5b582ce84af4297a58eafc.zip
LDAP: Add support for service lookups (non-enum)
Diffstat (limited to 'src')
-rw-r--r--src/providers/ldap/ldap_common.c33
-rw-r--r--src/providers/ldap/ldap_common.h12
-rw-r--r--src/providers/ldap/ldap_id.c30
-rw-r--r--src/providers/ldap/ldap_id_services.c290
-rw-r--r--src/providers/ldap/sdap.c11
-rw-r--r--src/providers/ldap/sdap.h13
-rw-r--r--src/providers/ldap/sdap_async.h17
-rw-r--r--src/providers/ldap/sdap_async_services.c485
8 files changed, 891 insertions, 0 deletions
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 3d7f9430f..786e06b3d 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 0ab0a5e12..cda21da41 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 feac63b67..db49e77d9 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 000000000..bd7bb5625
--- /dev/null
+++ b/src/providers/ldap/ldap_id_services.c
@@ -0,0 +1,290 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <errno.h>
+
+#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 c366dda0b..ba1c7911e 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 1827d7c6e..bbc59414a 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 2fd606bca..abf16b0c6 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 000000000..6fab4ace1
--- /dev/null
+++ b/src/providers/ldap/sdap_async_services.c
@@ -0,0 +1,485 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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;
+}