aboutsummaryrefslogtreecommitdiff
path: root/lib/ldapmap.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-10-08 00:54:16 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2012-10-08 00:54:16 +0300
commit9c97ac525930863abd56a77db9b3cdcf63c4cdd6 (patch)
tree9529dbcca59ca7a97ed638afc888aaf841cb3255 /lib/ldapmap.c
parent3f1639c2112f8242a542ca0a1b959f657d012cc9 (diff)
downloadeclat-9c97ac525930863abd56a77db9b3cdcf63c4cdd6.tar.gz
eclat-9c97ac525930863abd56a77db9b3cdcf63c4cdd6.tar.bz2
Implement LDAP backend.
* configure.ac: Check for openldap. * lib/Makefile.am (maps) [COND_LDAP]: Add ldapmap.c * lib/ldapmap.c: New file. * lib/libeclat.h (eclat_map_drv_ldap): New extern. * src/eclat.c (main) [WITH_LDAP]: Register eclat_map_drv_ldap.
Diffstat (limited to 'lib/ldapmap.c')
-rw-r--r--lib/ldapmap.c550
1 files changed, 550 insertions, 0 deletions
diff --git a/lib/ldapmap.c b/lib/ldapmap.c
new file mode 100644
index 0000000..a686718
--- /dev/null
+++ b/lib/ldapmap.c
@@ -0,0 +1,550 @@
+/* This file is part of Eclat.
+ Copyright (C) 2012 Sergey Poznyakoff.
+
+ Eclat 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, or (at your option)
+ any later version.
+
+ Eclat 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 Eclat. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "libeclat.h"
+#include "wordsplit.h"
+#include <ldap.h>
+#include <errno.h>
+#include <sysexits.h>
+
+enum { tls_no, tls_yes, tls_only };
+
+struct ldap_map {
+ unsigned long ldap_version;
+ char *uri;
+ char *base;
+ char *binddn;
+ char *bindpw;
+ int tls;
+ char *filter;
+ char *attr;
+ int dbg;
+ LDAP *ld;
+};
+
+static int
+cb_null(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ return 0;
+}
+
+static int
+cb_tls(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ int *tls = varptr;
+
+ if (cmd != grecs_callback_set_value) {
+ grecs_error(locus, 0, "Unexpected block statement");
+ return 1;
+ }
+ if (!value || value->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0, "expected a string");
+ return 1;
+ }
+ if (strcmp(value->v.string, "yes") == 0)
+ *tls = tls_yes;
+ else if (strcmp(value->v.string, "no") == 0)
+ *tls = tls_no;
+ else if (strcmp(value->v.string, "only") == 0)
+ *tls = tls_only;
+ else
+ grecs_error(locus, 0, "wrong value for tls statement");
+ return 0;
+}
+
+static char *typestr;
+
+static struct grecs_keyword ldapmap_kw[] = {
+ { "type", "arg", "Set map type",
+ grecs_type_string, GRECS_DFLT, NULL, 0, cb_null },
+ { "uri", "arg",
+ "Set LDAP URI",
+ grecs_type_string, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, uri) },
+ { "base", "arg",
+ "Set search base",
+ grecs_type_string, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, base) },
+ { "binddn", "arg",
+ "Set bind user name",
+ grecs_type_string, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, binddn) },
+ { "bindpw", "arg",
+ "Set bind password",
+ grecs_type_string, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, bindpw) },
+ { "tls", "arg",
+ "TLS mode",
+ grecs_type_string, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, tls), cb_tls },
+ { "filter", "arg",
+ "Filter expression",
+ grecs_type_string, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, filter) },
+ { "attr", "arg",
+ "Attribute to return",
+ grecs_type_string, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, attr) },
+ { "debug", "n",
+ "Set LDAP debug level",
+ grecs_type_int, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, dbg) },
+ { "ldap-version", "n",
+ "LDAP version to use",
+ grecs_type_int, GRECS_DFLT,
+ NULL, offsetof(struct ldap_map, ldap_version) },
+ { NULL }
+};
+
+static int
+ldap_map_config(int dbg, struct grecs_node *node, void *data)
+{
+ int i;
+ struct ldap_map *ldap_map, **return_ldap_map = data;
+
+ ldap_map = grecs_zalloc(sizeof(*ldap_map));
+ ldap_map->ldap_version = 3;
+ for (i = 0; ldapmap_kw[i].ident; i++)
+ ldapmap_kw[i].varptr = ldap_map;
+ if (grecs_tree_process(node->down, ldapmap_kw))
+ return eclat_map_failure;
+
+ *return_ldap_map = ldap_map;
+
+ return eclat_map_ok;
+}
+
+
+static void
+ldap_map_free(int dbg, void *data)
+{
+ free(data);
+}
+
+static void ldap_unbind(LDAP *ld);
+
+char *
+parse_ldap_uri(const char *uri)
+{
+ struct wordsplit ws;
+ LDAPURLDesc *ludlist, **ludp;
+ struct grecs_txtacc *acc;
+ char *ldapuri = NULL;
+ int rc;
+
+ rc = ldap_url_parse(uri, &ludlist);
+ if (rc != LDAP_URL_SUCCESS) {
+ err("cannot parse LDAP URL(s)=%s (%d)", uri, rc);
+ return NULL;
+ }
+
+ acc = grecs_txtacc_create();
+
+ for (ludp = &ludlist; *ludp; ) {
+ LDAPURLDesc *lud = *ludp;
+ char **tmp;
+
+ if (lud->lud_dn && lud->lud_dn[0]
+ && (lud->lud_host == NULL || lud->lud_host[0] == '\0')) {
+ /* if no host but a DN is provided, try
+ DNS SRV to gather the host list */
+ char *domain = NULL, *hostlist = NULL;
+ size_t i;
+
+ if (ldap_dn2domain(lud->lud_dn, &domain) ||
+ !domain) {
+ err("DNS SRV: cannot convert "
+ "DN=\"%s\" into a domain",
+ lud->lud_dn);
+ goto dnssrv_free0;
+ }
+
+ rc = ldap_domain2hostlist(domain, &hostlist);
+ if (rc) {
+ err("DNS SRV: cannot convert "
+ "domain=%s into a hostlist",
+ domain);
+ goto dnssrv_free0;
+ }
+
+ if (wordsplit(hostlist, &ws, WRDSF_DEFFLAGS)) {
+ err("DNS SRV: could not parse "
+ "hostlist=\"%s\": %s",
+ hostlist, wordsplit_strerror(&ws));
+ goto dnssrv_free;
+ }
+
+ for (i = 0; i < ws.ws_wordc; i++) {
+ grecs_txtacc_grow(acc, lud->lud_scheme,
+ strlen(lud->lud_scheme));
+ grecs_txtacc_grow(acc, "//", 2);
+ grecs_txtacc_grow(acc, ws.ws_wordv[i],
+ strlen(ws.ws_wordv[i]));
+ }
+
+ dnssrv_free0:
+ wordsplit_free(&ws);
+ dnssrv_free:
+ ber_memfree(hostlist);
+ ber_memfree(domain);
+ } else {
+ char *s = ldap_url_desc2str(lud);
+ grecs_txtacc_grow(acc, s, strlen(s));
+ }
+
+ *ludp = lud->lud_next;
+
+ lud->lud_next = NULL;
+ ldap_free_urldesc(lud);
+ }
+
+ if (ludlist) {
+ ldap_free_urldesc(ludlist);
+ return NULL;
+ }
+
+ grecs_txtacc_grow_char(acc, 0);
+ ldapuri = grecs_txtacc_finish(acc, 1);
+ grecs_txtacc_free(acc);
+
+ if (!ldapuri)
+ return NULL;
+ return ldapuri;
+}
+
+static LDAP *
+ldap_connect(int dbg, struct ldap_map *map)
+{
+ int rc;
+ char *ldapuri = NULL;
+ LDAP *ld = NULL;
+ int protocol = LDAP_VERSION3;
+ char *val;
+ unsigned long lval;
+
+ if (map->dbg) {
+ if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &map->dbg)
+ != LBER_OPT_SUCCESS )
+ err("cannot set LBER_OPT_DEBUG_LEVEL %d", &map->dbg);
+
+ if (ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &map->dbg)
+ != LDAP_OPT_SUCCESS )
+ err("could not set LDAP_OPT_DEBUG_LEVEL %d", map->dbg);
+ }
+
+ if (map->uri) {
+ ldapuri = parse_ldap_uri(map->uri);
+ if (!ldapuri)
+ return NULL;
+ }
+ debug(dbg, 2, ("constructed LDAP URI: %s",
+ ldapuri ? ldapuri : "<DEFAULT>"));
+
+ rc = ldap_initialize(&ld, ldapuri);
+ if (rc != LDAP_SUCCESS) {
+ err("cannot create LDAP session handle for "
+ "URI=%s (%d): %s",
+ ldapuri, rc, ldap_err2string(rc));
+ free(ldapuri);
+ return NULL;
+ }
+ free(ldapuri);
+
+ switch (map->ldap_version) {
+ case 2:
+ protocol = LDAP_VERSION2;
+ break;
+ case 3:
+ protocol = LDAP_VERSION3;
+ break;
+ default:
+ err("invalid LDAP version configured, defaulting to 3");
+ }
+
+ ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &protocol);
+
+ if (map->tls != tls_no) {
+ rc = ldap_start_tls_s(ld, NULL, NULL);
+ if (rc != LDAP_SUCCESS) {
+ char *msg = NULL;
+ ldap_get_option(ld,
+ LDAP_OPT_DIAGNOSTIC_MESSAGE,
+ (void*)&msg);
+ err("ldap_start_tls failed: %s", ldap_err2string(rc));
+ err("TLS diagnostics: %s", msg);
+ ldap_memfree(msg);
+
+ if (map->tls == tls_only) {
+ ldap_unbind(ld);
+ return NULL;
+ }
+ /* try to continue anyway */
+ }
+ }
+
+ /* FIXME: Timeouts, SASL, etc. */
+ return ld;
+}
+
+static int
+ldap_bind(int dbg, struct ldap_map *map)
+{
+ int msgid, ec, rc;
+ LDAPMessage *result;
+ LDAPControl **ctrls;
+ char msgbuf[256];
+ char *matched = NULL;
+ char *info = NULL;
+ char **refs = NULL;
+ struct berval passwd;
+ char *binddn;
+
+ msgbuf[0] = 0;
+
+ if (map->bindpw) {
+ passwd.bv_len = strlen(map->bindpw);
+ passwd.bv_val = map->bindpw;
+ } else {
+ passwd.bv_len = 0;
+ passwd.bv_val = NULL;
+ }
+ rc = ldap_sasl_bind(map->ld, map->binddn, LDAP_SASL_SIMPLE, &passwd,
+ NULL, NULL, &msgid);
+ if (msgid == -1) {
+ err("ldap_sasl_bind(SIMPLE) failed: %s", ldap_err2string(rc));
+ return 1;
+ }
+
+ if (ldap_result(map->ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1) {
+ err("ldap_result failed");
+ return 1;
+ }
+
+ rc = ldap_parse_result(map->ld, result, &ec, &matched, &info, &refs,
+ &ctrls, 1);
+ if (rc != LDAP_SUCCESS) {
+ err("ldap_parse_result failed: %s", ldap_err2string(rc));
+ return 1;
+ }
+
+ if (ctrls)
+ ldap_controls_free(ctrls);
+
+ if (ec != LDAP_SUCCESS
+ || msgbuf[0]
+ || (matched && matched[0])
+ || (info && info[0])
+ || refs) {
+ /* FIXME: Use debug output for that */
+ debug(dbg, 2, ("ldap_bind: %s (%d)%s",
+ ldap_err2string(ec), ec, msgbuf));
+
+ if (matched && *matched)
+ debug(dbg, 2, ("matched DN: %s", matched));
+
+ if (info && *info)
+ debug(dbg, 2, ("additional info: %s", info));
+
+ if (refs && *refs) {
+ int i;
+ debug(dbg, 3, ("referrals:"));
+ for (i = 0; refs[i]; i++)
+ debug(dbg, 3, ("%s", refs[i]));
+ }
+ }
+
+ if (matched)
+ ber_memfree(matched);
+ if (info)
+ ber_memfree(info);
+ if (refs)
+ ber_memvfree((void **)refs);
+
+ return !(ec == LDAP_SUCCESS);
+}
+
+static void
+ldap_unbind(LDAP *ld)
+{
+ if (ld) {
+ ldap_set_option(ld, LDAP_OPT_SERVER_CONTROLS, NULL);
+ ldap_unbind_ext(ld, NULL, NULL);
+ }
+}
+
+static int
+ldap_map_open(int dbg, void *data)
+{
+ struct ldap_map *map = data;
+
+ map->ld = ldap_connect(dbg, map);
+ if (!map->ld)
+ return eclat_map_failure;
+ if (ldap_bind(dbg, map))
+ return eclat_map_failure;
+ return eclat_map_ok;
+}
+
+static int
+ldap_map_close(int dbg, void *data)
+{
+ struct ldap_map *map = data;
+ ldap_unbind(map->ld);
+ return 0;
+}
+
+static void
+trimnl(char *s)
+{
+ size_t len = strlen(s);
+ while (len > 0 && s[len-1] == '\n')
+ --len;
+ s[len] = 0;
+}
+
+static int
+keycmp(const void *a, const void *b)
+{
+ return strcmp(*(char**)a, *(char**)b);
+}
+
+static char **
+get_ldap_attrs(LDAP *ld, LDAPMessage *msg, const char *attr)
+{
+ int rc, i, count;
+ BerElement *ber = NULL;
+ struct berval bv;
+ char *ufn = NULL;
+ char **ret;
+ struct berval **values;
+
+ rc = ldap_get_dn_ber(ld, msg, &ber, &bv);
+ ufn = ldap_dn2ufn(bv.bv_val);
+ ldap_memfree(ufn);
+
+ values = ldap_get_values_len(ld, msg, attr);
+ if (!values) {
+ err("LDAP attribute `%s' has NULL value", attr);
+ return NULL;
+ }
+
+ for (count = 0; values[count]; count++)
+ ;
+
+ ret = grecs_calloc(count + 1, sizeof(ret[0]));
+
+ for (i = 0; values[i]; i++) {
+ char *p = grecs_malloc(values[i]->bv_len + 1);
+ memcpy(p, values[i]->bv_val, values[i]->bv_len);
+ p[values[i]->bv_len] = 0;
+ trimnl(p);
+ ret[i] = p;
+ }
+
+ if (i < count) {
+ while (--i >= 0)
+ free(ret[i]);
+ free(ret);
+ ret = NULL;
+ } else {
+ ret[i] = NULL;
+ qsort(ret, i, sizeof(ret[0]), keycmp);
+ }
+
+ ldap_value_free_len(values);
+ return ret;
+}
+
+static int
+ldap_map_get(int dbg, void *data, const char *key, char **return_value)
+{
+ struct ldap_map *map = data;
+ int rc, i;
+ LDAPMessage *res, *msg;
+ ber_int_t msgid;
+ char *attrs[2];
+ char **ret;
+ const char *kve[3];
+ struct wordsplit ws;
+
+ kve[0] = "key";
+ kve[1] = key;
+ kve[2] = NULL;
+
+ ws.ws_env = kve;
+
+ if (wordsplit(map->filter, &ws,
+ WRDSF_NOSPLIT | WRDSF_NOCMD |
+ WRDSF_ENV | WRDSF_ENV_KV))
+ die(EX_SOFTWARE, "error expanding filter: %s",
+ wordsplit_strerror(&ws));
+
+ attrs[0] = (char*) map->attr;
+ attrs[1] = NULL;
+ rc = ldap_search_ext(map->ld, map->base, LDAP_SCOPE_SUBTREE,
+ ws.ws_wordv[0], attrs, 0,
+ NULL, NULL, NULL, -1, &msgid);
+ wordsplit_free(&ws);
+
+ if (rc != LDAP_SUCCESS) {
+ err("ldap_search_ext: %s", ldap_err2string(rc));
+ return eclat_map_failure;
+ }
+
+ rc = ldap_result(map->ld, msgid, LDAP_MSG_ALL, NULL, &res);
+ if (rc < 0) {
+ err("ldap_result failed");
+ return eclat_map_failure;
+ }
+
+ msg = ldap_first_entry(map->ld, res);
+ if (!msg) {
+ ldap_msgfree(res);
+ return eclat_map_failure;
+ }
+
+ ret = get_ldap_attrs(map->ld, msg, map->attr);
+
+ ldap_msgfree(res);
+
+ if (!ret || !ret[0])
+ return eclat_map_not_found;
+
+ *return_value = ret[0];
+
+ for (i = 1; ret[i]; i++)
+ free(ret[i]);
+ free(ret);
+ return eclat_map_ok;
+}
+
+struct eclat_map_drv eclat_map_drv_ldap = {
+ "ldap",
+ ldap_map_config,
+ ldap_map_open,
+ ldap_map_close,
+ ldap_map_get,
+ ldap_map_free
+};
+

Return to:

Send suggestions and report system problems to the System administrator.