summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/db/sysdb_services.c692
-rw-r--r--src/db/sysdb_services.h81
-rw-r--r--src/tests/sysdb-tests.c358
3 files changed, 1131 insertions, 0 deletions
diff --git a/src/db/sysdb_services.c b/src/db/sysdb_services.c
new file mode 100644
index 00000000..efdf7f11
--- /dev/null
+++ b/src/db/sysdb_services.c
@@ -0,0 +1,692 @@
+/*
+ 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_private.h"
+#include "db/sysdb_services.h"
+
+errno_t
+sysdb_svc_add(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols,
+ struct ldb_dn **dn);
+
+static errno_t
+sysdb_svc_update(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ int port,
+ const char **aliases,
+ const char **protocols);
+
+errno_t
+sysdb_svc_remove_alias(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *alias);
+
+errno_t
+sysdb_getservbyname(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *name,
+ const char *proto,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_SVC_ATTRS;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ char *sanitized_name;
+ char *sanitized_proto;
+
+ *_res = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
+ SYSDB_TMPL_SVC_BASE,
+ sysdb->domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (proto) {
+ ret = sss_filter_sanitize(tmp_ctx, proto, &sanitized_proto);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ lret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs,
+ SYSDB_SVC_BYNAME_FILTER,
+ proto?sanitized_proto:"*",
+ sanitized_name, sanitized_name);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_getservbyport(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ int port,
+ const char *proto,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_SVC_ATTRS;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ char *sanitized_proto = NULL;
+
+ if (port <= 0) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
+ SYSDB_TMPL_SVC_BASE,
+ sysdb->domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (proto) {
+ ret = sss_filter_sanitize(tmp_ctx, proto, &sanitized_proto);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ lret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs,
+ SYSDB_SVC_BYPORT_FILTER,
+ sanitized_proto?sanitized_proto:"*",
+ (unsigned int) port);
+ if (lret) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_store_service(struct sysdb_ctx *sysdb,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+ errno_t sret;
+ TALLOC_CTX *tmp_ctx;
+ bool in_transaction = false;
+ struct ldb_result *res = NULL;
+ const char *name;
+ unsigned int i;
+ struct ldb_dn *update_dn = NULL;
+ struct sysdb_attrs *attrs;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) goto done;
+
+ in_transaction = true;
+
+ /* Check that the port is unique
+ * If the port appears for any service other than
+ * the one matching the primary_name, we need to
+ * remove them so that getservbyport() can work
+ * properly. Last entry saved to the cache should
+ * always "win".
+ */
+ ret = sysdb_getservbyport(tmp_ctx, sysdb, port, NULL, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret != ENOENT) {
+ if (res->count != 1) {
+ /* Somehow the cache has multiple entries with
+ * the same port. This is corrupted. We'll delete
+ * them all to sort it out.
+ */
+ for (i = 0; i < res->count; i++) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ ("Corrupt cache entry [%s] detected. Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn)));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not delete corrupt cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn)));
+ goto done;
+ }
+ }
+ } else {
+ /* Check whether this is the same name as we're currently
+ * saving to the cache.
+ */
+ name = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_NAME,
+ NULL);
+ if (!name || strcmp(name, primary_name) != 0) {
+
+ if (!name) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("A service with no name?\n"));
+ /* Corrupted */
+ }
+
+ /* Either this is a corrupt entry or it's another service
+ * claiming ownership of this port. In order to account
+ * for port reassignments, we need to delete the old entry.
+ */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ ("Corrupt or replaced cache entry [%s] detected. "
+ "Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[0]->dn)));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[0]->dn)));
+ }
+ }
+ }
+ }
+ talloc_zfree(res);
+
+ /* Ok, ports should now be unique. Now look
+ * the service up by name to determine if we
+ * need to update existing entries or modify
+ * aliases.
+ */
+ ret = sysdb_getservbyname(tmp_ctx, sysdb, primary_name, NULL, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret != ENOENT) { /* Found entries */
+ for (i = 0; i < res->count; i++) {
+ /* Check whether this is the same name as we're currently
+ * saving to the cache.
+ */
+ name = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_NAME,
+ NULL);
+ if (!name) {
+
+ /* Corrupted */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("A service with no name?\n"));
+ DEBUG(SSSDBG_TRACE_FUNC,
+ ("Corrupt cache entry [%s] detected. Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn)));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not delete corrupt cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn)));
+ goto done;
+ }
+ } else if (strcmp(name, primary_name) == 0) {
+ /* This is the same service name, so we need
+ * to update this entry with the values
+ * provided.
+ */
+ if(update_dn) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Two existing services with the same name: [%s]? "
+ "Deleting both.\n",
+ primary_name));
+
+ /* Delete the entry from the previous pass */
+ ret = sysdb_delete_entry(sysdb, update_dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ update_dn)));
+ goto done;
+ }
+
+ /* Delete the new entry as well */
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn)));
+ goto done;
+ }
+
+ update_dn = NULL;
+ } else {
+ update_dn = talloc_steal(tmp_ctx, res->msgs[i]->dn);
+ }
+ } else {
+ /* Another service is claiming this name as an alias.
+ * In order to account for aliases being promoted to
+ * primary names, we need to make sure to remove the
+ * old alias entry.
+ */
+ ret = sysdb_svc_remove_alias(sysdb,
+ res->msgs[i]->dn,
+ primary_name);
+ if (ret != EOK) goto done;
+ }
+ }
+ talloc_zfree(res);
+ }
+
+ if (update_dn) {
+ /* Update the existing entry */
+ ret = sysdb_svc_update(sysdb, update_dn, port, aliases, protocols);
+ } else {
+ /* Add a new entry */
+ ret = sysdb_svc_add(tmp_ctx, sysdb, primary_name, port,
+ aliases, protocols, &update_dn);
+ }
+ if (ret != EOK) goto done;
+
+ /* Set the cache timeout */
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) goto done;
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) goto done;
+
+ ret = sysdb_set_entry_attr(sysdb, update_dn, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) goto done;
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret == EOK) in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Could not cancel transaction\n"));
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct ldb_dn *
+sysdb_svc_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
+ const char *domain, const char *name)
+{
+ errno_t ret;
+ char *clean_name;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, name, &clean_name);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, sysdb->ldb, SYSDB_TMPL_SVC,
+ clean_name, domain);
+ talloc_free(clean_name);
+
+ return dn;
+}
+
+errno_t
+sysdb_svc_add(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols,
+ struct ldb_dn **dn)
+{
+ errno_t ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ unsigned long i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* svc dn */
+ msg->dn = sysdb_svc_dn(sysdb, msg, sysdb->domain->name, primary_name);
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Objectclass */
+ ret = add_string(msg, LDB_FLAG_MOD_ADD,
+ SYSDB_OBJECTCLASS, SYSDB_SVC_CLASS);
+ if (ret != EOK) goto done;
+
+ /* Set the primary name */
+ ret = add_string(msg, LDB_FLAG_MOD_ADD,
+ SYSDB_NAME, primary_name);
+ if (ret != EOK) goto done;
+
+ /* Set the port number */
+ ret = add_ulong(msg, LDB_FLAG_MOD_ADD,
+ SYSDB_SVC_PORT, port);
+ if (ret != EOK) goto done;
+
+ /* If this service has any aliases, include them */
+ if (aliases && aliases[0]) {
+ /* Set the name aliases */
+ lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ for (i=0; aliases[i]; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_NAME_ALIAS, aliases[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+ }
+
+ /* Set the protocols */
+ lret = ldb_msg_add_empty(msg, SYSDB_SVC_PROTO,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ for (i=0; protocols[i]; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_SVC_PROTO, protocols[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ /* creation time */
+ ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME,
+ (unsigned long)time(NULL));
+ if (ret) goto done;
+
+ lret = ldb_add(sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(lret);
+
+ if (ret == EOK && dn) {
+ *dn = talloc_steal(mem_ctx, msg->dn);
+ }
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ ("Error: %d (%s)\n", ret, strerror(ret)));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sysdb_svc_update(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ int port,
+ const char **aliases,
+ const char **protocols)
+{
+ errno_t ret;
+ struct ldb_message *msg;
+ int lret;
+ unsigned int i;
+
+ if (!dn || !protocols || !protocols[0]) {
+ return EINVAL;
+ }
+
+ msg = ldb_msg_new(NULL);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ /* Update the port */
+ ret = add_ulong(msg, SYSDB_MOD_REP,
+ SYSDB_SVC_PORT, port);
+ if (ret != EOK) goto done;
+
+ if (aliases && aliases[0]) {
+ /* Update the aliases */
+ lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS, SYSDB_MOD_REP, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; aliases[i]; i++) {
+ lret = ldb_msg_add_fmt(msg, SYSDB_NAME_ALIAS, "%s", aliases[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ /* Update the protocols */
+ lret = ldb_msg_add_empty(msg, SYSDB_SVC_PROTO, SYSDB_MOD_REP, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; protocols[i]; i++) {
+ lret = ldb_msg_add_fmt(msg, SYSDB_SVC_PROTO, "%s", protocols[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ lret = ldb_modify(sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ ("Error: %d (%s)\n", ret, strerror(ret)));
+ }
+ talloc_free(msg);
+ return ret;
+}
+
+errno_t
+sysdb_svc_remove_alias(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *alias)
+{
+ errno_t ret;
+ struct ldb_message *msg;
+ int lret;
+
+ msg = ldb_msg_new(NULL);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ ret = add_string(msg, SYSDB_MOD_DEL,
+ SYSDB_NAME_ALIAS, alias);
+ if (ret != EOK) goto done;
+
+ lret = ldb_modify(sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ ("Error: %d (%s)\n", ret, strerror(ret)));
+ }
+ talloc_zfree(msg);
+ return ret;
+}
+
+errno_t
+sysdb_svc_delete(struct sysdb_ctx *sysdb,
+ const char *name,
+ int port,
+ const char *proto)
+{
+ errno_t ret, sret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ unsigned int i;
+ bool in_transaction;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) goto done;
+
+ in_transaction = true;
+
+ if (name) {
+ ret = sysdb_getservbyname(tmp_ctx, sysdb, name, proto, &res);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == ENOENT) {
+ /* Doesn't exist in the DB. Nothing to do */
+ ret = EOK;
+ goto done;
+ }
+ } else {
+ ret = sysdb_getservbyport(tmp_ctx, sysdb, port, proto, &res);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == ENOENT) {
+ /* Doesn't exist in the DB. Nothing to do */
+ ret = EOK;
+ goto done;
+ }
+ }
+
+ /* There should only be one matching entry,
+ * but if there are multiple, we should delete
+ * them all to de-corrupt the DB.
+ */
+ for (i = 0; i < res->count; i++) {
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, false);
+ if (ret != EOK) goto done;
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Could not cancel transaction\n"));
+ }
+ }
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ ("Error: %d (%s)\n", ret, strerror(ret)));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_services.h b/src/db/sysdb_services.h
new file mode 100644
index 00000000..40612355
--- /dev/null
+++ b/src/db/sysdb_services.h
@@ -0,0 +1,81 @@
+/*
+ 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/>.
+*/
+
+#ifndef SYSDB_SERVICES_H_
+#define SYSDB_SERVICES_H_
+
+#define SYSDB_SVC_CLASS "service"
+#define SYSDB_SVC_CONTAINER "cn=services"
+#define SYSDB_SC "objectclass="SYSDB_SVC_CLASS
+
+#define SYSDB_SVC_PORT "servicePort"
+#define SYSDB_SVC_PROTO "serviceProtocol"
+
+#define SYSDB_TMPL_SVC_BASE SYSDB_SVC_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_SVC SYSDB_NAME"=%s,"SYSDB_TMPL_SVC_BASE
+
+#define SYSDB_SVC_BYNAME_FILTER "(&("SYSDB_SC")("SYSDB_SVC_PROTO"=%s)(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_SVC_BYPORT_FILTER "(&("SYSDB_SC")("SYSDB_SVC_PROTO"=%s)("SYSDB_SVC_PORT"=%u))"
+
+
+#define SYSDB_SVC_ATTRS { \
+ SYSDB_NAME, \
+ SYSDB_NAME_ALIAS, \
+ SYSDB_SVC_PORT, \
+ SYSDB_SVC_PROTO, \
+ SYSDB_DEFAULT_ATTRS, \
+ NULL }
+
+errno_t
+sysdb_getservbyname(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *name,
+ const char *proto,
+ struct ldb_result **_res);
+
+errno_t
+sysdb_getservbyport(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ int port,
+ const char *proto,
+ struct ldb_result **_res);
+
+errno_t
+sysdb_store_service(struct sysdb_ctx *sysdb,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols,
+ uint64_t cache_timeout,
+ time_t now);
+
+struct ldb_dn *
+sysdb_svc_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
+ const char *domain, const char *name);
+
+errno_t
+sysdb_svc_delete(struct sysdb_ctx *sysdb,
+ const char *name,
+ int port,
+ const char *proto);
+
+#endif /* SYSDB_SERVICES_H_ */
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 9c080c9e..8b78ff32 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -29,6 +29,7 @@
#include "util/util.h"
#include "confdb/confdb_setup.h"
#include "db/sysdb_private.h"
+#include "db/sysdb_services.h"
#include "tests/common.h"
#define TESTS_PATH "tests_sysdb"
@@ -2811,6 +2812,356 @@ START_TEST(test_odd_characters)
}
END_TEST
+/* == SERVICE TESTS == */
+void services_check_match(struct sysdb_test_ctx *test_ctx,
+ bool by_name,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols)
+{
+ errno_t ret;
+ unsigned int i, j;
+ bool matched;
+ const char *ret_name;
+ int ret_port;
+ struct ldb_result *res;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+
+ if (by_name) {
+ /* Look up the service by name */
+ ret = sysdb_getservbyname(test_ctx, test_ctx->sysdb, primary_name,
+ NULL, &res);
+ fail_if(ret != EOK, "sysdb_getservbyname error [%s]\n",
+ strerror(ret));
+ } else {
+ /* Look up the newly-added service by port */
+ ret = sysdb_getservbyport(test_ctx, test_ctx->sysdb,
+ port, NULL, &res);
+ fail_if(ret != EOK, "sysdb_getservbyport error [%s]\n",
+ strerror(ret));
+ }
+ fail_if(res == NULL, "ENOMEM");
+ fail_if(res->count != 1);
+
+ /* Make sure the returned entry matches */
+ msg = res->msgs[0];
+ ret_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ fail_if(ret_name == NULL);
+ fail_unless(strcmp(ret_name, primary_name) == 0);
+
+ ret_port = ldb_msg_find_attr_as_int(msg, SYSDB_SVC_PORT, 0);
+ fail_if (ret_port != port);
+
+ el = ldb_msg_find_element(msg, SYSDB_NAME_ALIAS);
+ for (i = 0; i < el->num_values; i++) {
+ matched = false;
+ for (j = 0; aliases[j]; j++) {
+ if (strcmp(aliases[j], (const char *)el->values[i].data) == 0) {
+ matched = true;
+ }
+ }
+ fail_if(!matched, "Unexpected value in LDB entry: [%s]",
+ (const char *)el->values[i].data);
+ }
+
+ el = ldb_msg_find_element(msg, SYSDB_SVC_PROTO);
+ for (i = 0; i < el->num_values; i++) {
+ matched = false;
+ for (j = 0; protocols[j]; j++) {
+ if (strcmp(protocols[j], (const char *)el->values[i].data) == 0) {
+ matched = true;
+ }
+ }
+ fail_if(!matched, "Unexpected value in LDB entry: [%s]",
+ (const char *)el->values[i].data);
+ }
+}
+
+#define services_check_match_name(test_ctx, primary_name, port, aliases, protocols) \
+ do { \
+ services_check_match(test_ctx, true, primary_name, port, aliases, protocols); \
+ } while(0);
+
+#define services_check_match_port(test_ctx, primary_name, port, aliases, protocols) \
+ do { \
+ services_check_match(test_ctx, false, primary_name, port, aliases, protocols); \
+ } while(0);
+
+START_TEST(test_sysdb_add_services)
+{
+ errno_t ret;
+ struct sysdb_test_ctx *test_ctx;
+ char *primary_name;
+ const char **aliases;
+ const char **protocols;
+ int port = 3890;
+
+ /* Setup */
+ ret = setup_sysdb_tests(&test_ctx);
+ fail_if(ret != EOK, "Could not set up the test");
+
+ primary_name = talloc_asprintf(test_ctx, "test_service");
+ fail_if(primary_name == NULL);
+
+ aliases = talloc_array(test_ctx, const char *, 3);
+ fail_if(aliases == NULL);
+
+ aliases[0] = talloc_asprintf(aliases, "test_service_alias1");
+ fail_if(aliases[0] == NULL);
+
+ aliases[1] = talloc_asprintf(aliases, "test_service_alias2");
+ fail_if(aliases[1] == NULL);
+
+ aliases[2] = NULL;
+
+ protocols = talloc_array(test_ctx, const char *, 3);
+ fail_if(protocols == NULL);
+
+ protocols[0] = talloc_asprintf(protocols, "tcp");
+ fail_if(protocols[0] == NULL);
+
+ protocols[1] = talloc_asprintf(protocols, "udp");
+ fail_if(protocols[1] == NULL);
+
+ protocols[2] = NULL;
+
+ ret = sysdb_transaction_start(test_ctx->sysdb);
+ fail_if(ret != EOK);
+
+ ret = sysdb_svc_add(NULL, test_ctx->sysdb,
+ primary_name, port,
+ aliases, protocols,
+ NULL);
+ fail_unless(ret == EOK, "sysdb_svc_add error [%s]\n", strerror(ret));
+
+ /* Search by name and make sure the results match */
+ services_check_match_name(test_ctx,
+ primary_name, port,
+ aliases, protocols);
+
+ /* Search by port and make sure the results match */
+ services_check_match_port(test_ctx,
+ primary_name, port,
+ aliases, protocols);
+
+ ret = sysdb_transaction_commit(test_ctx->sysdb);
+ fail_if (ret != EOK);
+
+ /* Clean up after ourselves (and test deleting by name)
+ *
+ * We have to do this after the transaction, because LDB
+ * doesn't like adding and deleting the same entry in a
+ * single transaction.
+ */
+ ret = sysdb_svc_delete(test_ctx->sysdb, primary_name, 0, NULL);
+ fail_if(ret != EOK);
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(test_sysdb_store_services)
+{
+ errno_t ret;
+ struct sysdb_test_ctx *test_ctx;
+ const char *primary_name = "test_store_service";
+ const char *alt_primary_name = "alt_test_store_service";
+ const char **aliases;
+ const char **protocols;
+ int port = 3890;
+ int altport = 3891;
+
+ /* Setup */
+ ret = setup_sysdb_tests(&test_ctx);
+ fail_if(ret != EOK, "Could not set up the test");
+
+ aliases = talloc_array(test_ctx, const char *, 3);
+ fail_if(aliases == NULL);
+
+ aliases[0] = talloc_asprintf(aliases, "test_service_alias1");
+ fail_if(aliases[0] == NULL);
+
+ aliases[1] = talloc_asprintf(aliases, "test_service_alias2");
+ fail_if(aliases[1] == NULL);
+
+ aliases[2] = NULL;
+
+ protocols = talloc_array(test_ctx, const char *, 3);
+ fail_if(protocols == NULL);
+
+ protocols[0] = talloc_asprintf(protocols, "tcp");
+ fail_if(protocols[0] == NULL);
+
+ protocols[1] = talloc_asprintf(protocols, "udp");
+ fail_if(protocols[1] == NULL);
+
+ protocols[2] = NULL;
+
+ ret = sysdb_transaction_start(test_ctx->sysdb);
+ fail_if(ret != EOK);
+
+ /* Store this group (which will add it) */
+ ret = sysdb_store_service(test_ctx->sysdb,
+ primary_name, port,
+ aliases, protocols,
+ 1, 1);
+ fail_if (ret != EOK);
+
+ /* Search by name and make sure the results match */
+ services_check_match_name(test_ctx,
+ primary_name, port,
+ aliases, protocols);
+
+ /* Search by port and make sure the results match */
+ services_check_match_port(test_ctx,
+ primary_name, port,
+ aliases, protocols);
+
+ /* Change the service name */
+ ret = sysdb_store_service(test_ctx->sysdb,
+ alt_primary_name, port,
+ aliases, protocols,
+ 1, 1);
+ fail_if (ret != EOK, "[%s]", strerror(ret));
+
+ services_check_match_name(test_ctx,
+ alt_primary_name, port,
+ aliases, protocols);
+
+ /* Search by port and make sure the results match */
+ services_check_match_port(test_ctx,
+ alt_primary_name, port,
+ aliases, protocols);
+
+
+ /* Change it back */
+ ret = sysdb_store_service(test_ctx->sysdb,
+ primary_name, port,
+ aliases, protocols,
+ 1, 1);
+ fail_if (ret != EOK, "[%s]", strerror(ret));
+
+ /* Change the port number */
+ ret = sysdb_store_service(test_ctx->sysdb,
+ primary_name, altport,
+ aliases, protocols,
+ 1, 1);
+ fail_if (ret != EOK, "[%s]", strerror(ret));
+
+ /* Search by name and make sure the results match */
+ services_check_match_name(test_ctx,
+ primary_name, altport,
+ aliases, protocols);
+
+ /* Search by port and make sure the results match */
+ services_check_match_port(test_ctx,
+ primary_name, altport,
+ aliases, protocols);
+
+ /* TODO: Test changing aliases and protocols */
+
+ ret = sysdb_transaction_commit(test_ctx->sysdb);
+ fail_if(ret != EOK, "[%s]", strerror(ret));
+
+ /* Clean up after ourselves (and test deleting by port)
+ *
+ * We have to do this after the transaction, because LDB
+ * doesn't like adding and deleting the same entry in a
+ * single transaction.
+ */
+ ret = sysdb_svc_delete(test_ctx->sysdb, NULL, altport, NULL);
+ fail_if(ret != EOK);
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(test_sysdb_svc_remove_alias)
+{
+ errno_t ret;
+ struct sysdb_test_ctx *test_ctx;
+ const char *primary_name = "remove_alias_test";
+ const char **aliases;
+ const char **protocols;
+ int port = 3990;
+ struct ldb_dn *dn;
+
+ /* Setup */
+ ret = setup_sysdb_tests(&test_ctx);
+ fail_if(ret != EOK, "Could not set up the test");
+
+ aliases = talloc_array(test_ctx, const char *, 3);
+ fail_if(aliases == NULL);
+
+ aliases[0] = talloc_asprintf(aliases, "remove_alias_alias1");
+ fail_if(aliases[0] == NULL);
+
+ aliases[1] = talloc_asprintf(aliases, "remove_alias_alias2");
+ fail_if(aliases[1] == NULL);
+
+ aliases[2] = NULL;
+
+ protocols = talloc_array(test_ctx, const char *, 3);
+ fail_if(protocols == NULL);
+
+ protocols[0] = talloc_asprintf(protocols, "tcp");
+ fail_if(protocols[0] == NULL);
+
+ protocols[1] = talloc_asprintf(protocols, "udp");
+ fail_if(protocols[1] == NULL);
+
+ protocols[2] = NULL;
+
+ ret = sysdb_transaction_start(test_ctx->sysdb);
+ fail_if(ret != EOK);
+
+ ret = sysdb_svc_add(NULL, test_ctx->sysdb,
+ primary_name, port,
+ aliases, protocols,
+ NULL);
+ fail_unless(ret == EOK, "sysdb_svc_add error [%s]\n", strerror(ret));
+
+ /* Search by name and make sure the results match */
+ services_check_match_name(test_ctx,
+ primary_name, port,
+ aliases, protocols);
+
+ /* Search by port and make sure the results match */
+ services_check_match_port(test_ctx,
+ primary_name, port,
+ aliases, protocols);
+
+ /* Now remove an alias */
+ dn = sysdb_svc_dn(test_ctx->sysdb, test_ctx, test_ctx->domain->name, primary_name);
+ fail_if (dn == NULL);
+
+ ret = sysdb_svc_remove_alias(test_ctx->sysdb, dn, aliases[1]);
+ fail_if (ret != EOK, "[%s]", strerror(ret));
+ sysdb_transaction_commit(test_ctx->sysdb);
+ sysdb_transaction_start(test_ctx->sysdb);
+
+ /* Set aliases[1] to NULL to perform validation checks */
+ aliases[1] = NULL;
+
+ /* Search by name and make sure the results match */
+ services_check_match_name(test_ctx,
+ primary_name, port,
+ aliases, protocols);
+
+ /* Search by port and make sure the results match */
+ services_check_match_port(test_ctx,
+ primary_name, port,
+ aliases, protocols);
+
+ ret = sysdb_transaction_commit(test_ctx->sysdb);
+ fail_if(ret != EOK);
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
START_TEST(test_sysdb_has_enumerated)
{
errno_t ret;
@@ -3052,6 +3403,13 @@ Suite *create_sysdb_suite(void)
/* Remove the other half by DN */
tcase_add_loop_test(tc_sysdb, test_sysdb_remove_netgroup_entry, 27005, 27010);
+/* ===== SERVICE TESTS ===== */
+
+ /* Create a new service */
+ tcase_add_test(tc_sysdb, test_sysdb_add_services);
+ tcase_add_test(tc_sysdb, test_sysdb_store_services);
+ tcase_add_test(tc_sysdb, test_sysdb_svc_remove_alias);
+
/* Add all test cases to the test suite */
suite_add_tcase(s, tc_sysdb);