]> git.sur5r.net Git - openldap/commitdiff
New ADremap overlay
authorHoward Chu <hyc@openldap.org>
Fri, 3 Jul 2015 19:11:25 +0000 (20:11 +0100)
committerHoward Chu <hyc@openldap.org>
Fri, 3 Jul 2015 19:11:25 +0000 (20:11 +0100)
contrib/ConfigOIDs
contrib/slapd-modules/adremap/Makefile [new file with mode: 0644]
contrib/slapd-modules/adremap/adremap.c [new file with mode: 0644]
contrib/slapd-modules/adremap/slapo-adremap.5 [new file with mode: 0644]

index a04675a3d786862ce510cb020e65237c35228c73..6dd4a9a4a10f7a96558a4db6e8b8b4a8bbb73418 100644 (file)
@@ -5,3 +5,4 @@ OLcfgCt{Oc|At}:2        autogroup
 OLcfgCt{Oc|At}:3       nssov
 OLcfgCt{Oc|At}:4       cloak
 OLcfgCt{Oc|At}:5       lastbind
+OLcfgCt{Oc|At}:6       adremap
diff --git a/contrib/slapd-modules/adremap/Makefile b/contrib/slapd-modules/adremap/Makefile
new file mode 100644 (file)
index 0000000..e4cab3b
--- /dev/null
@@ -0,0 +1,56 @@
+# $OpenLDAP$
+# Copyright 2015 Howard Chu <hyc@symas.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap_r/libldap_r.la \
+       $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2 -Wall
+DEFS = -DSLAPD_OVER_ADREMAP=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = adremap.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+       $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+adremap.la: adremap.lo
+       $(LIBTOOL) --mode=link $(CC) $(OPT) -version-info $(LTVER) \
+       -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+       rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+       mkdir -p $(DESTDIR)$(moduledir)
+       for p in $(PROGRAMS) ; do \
+               $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+       done
+
diff --git a/contrib/slapd-modules/adremap/adremap.c b/contrib/slapd-modules/adremap/adremap.c
new file mode 100644 (file)
index 0000000..38c95aa
--- /dev/null
@@ -0,0 +1,352 @@
+/* adremap.c - Case-folding and DN-value remapping for AD proxies */
+/* $OpenLDAP$ */
+/*
+ * Copyright 2015 Howard Chu <hyc@symas.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+/*
+ * This file implements an overlay that performs two remapping functions
+ * to allow older POSIX clients to use Microsoft AD:
+ * 1: downcase the values of a configurable list of attributes
+ * 2: dereference some DN-valued attributes and convert to their simple names
+ *        e.g. generate memberUid based on member
+ */
+
+#ifdef SLAPD_OVER_ADREMAP
+
+#include <ldap.h>
+#include "lutil.h"
+#include "slap.h"
+#include <ac/errno.h>
+#include <ac/time.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include "config.h"
+
+typedef struct adremap_dnv {
+       struct adremap_dnv *ad_next;
+       AttributeDescription *ad_dnattr;        /* DN-valued attr to deref */
+       AttributeDescription *ad_deref;         /* target attr's value to retrieve */
+       AttributeDescription *ad_newattr;       /* New attr to collect new values */
+} adremap_dnv;
+/* example: member uid memberUid */
+
+typedef struct adremap_case {
+       struct adremap_case *ac_next;
+       AttributeDescription *ac_attr;
+} adremap_case;
+
+/* Per-instance configuration information */
+typedef struct adremap_info {
+       adremap_case *ai_case;  /* attrs to downcase */
+       adremap_dnv *ai_dnv;    /* DN attrs to remap */
+} adremap_info;
+
+enum {
+       ADREMAP_CASE = 1,
+       ADREMAP_DNV
+};
+
+static ConfigDriver adremap_cf_case;
+static ConfigDriver adremap_cf_dnv;
+
+/* configuration attribute and objectclass */
+static ConfigTable adremapcfg[] = {
+       { "adremap-downcase", "attrs", 2, 0, 0,
+         ARG_MAGIC|ADREMAP_CASE, adremap_cf_case,
+         "( OLcfgCtAt:6.1 "
+         "NAME 'olcADremapDowncase' "
+         "DESC 'List of attributes to casefold to lower case' "
+         "SYNTAX OMsDirectoryString )", NULL, NULL },
+       { "adremap-dnmap", "dnattr simpleattr newattr", 4, 4, 0,
+         ARG_MAGIC|ADREMAP_DNV, adremap_cf_dnv,
+         "( OLcfgCtAt:6.2 "
+         "NAME 'olcADremapDNmap' "
+         "DESC 'DN attr to map, attr from target to use, attr to generate' "
+         "SYNTAX OMsDirectoryString )", NULL, NULL },
+       { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs adremapocs[] = {
+       { "( OLcfgCtOc:6.1 "
+         "NAME 'olcADremapConfig' "
+         "DESC 'AD remap configuration' "
+         "SUP olcOverlayConfig "
+         "MAY ( olcADremapDowncase $ olcADremapDNmap ) )",
+         Cft_Overlay, adremapcfg, NULL, NULL },
+       { NULL, 0, NULL }
+};
+
+static int
+adremap_cf_case(ConfigArgs *c)
+{
+       BackendDB *be = (BackendDB *)c->be;
+       slap_overinst *on = (slap_overinst *)c->bi;
+       adremap_info *ai = on->on_bi.bi_private;
+       adremap_case *ac, **a2;
+       int rc = ARG_BAD_CONF;
+
+       switch(c->op) {
+       case SLAP_CONFIG_EMIT:
+               for (ac = ai->ai_case; ac; ac=ac->ac_next) {
+                       rc = value_add_one(&c->rvalue_vals, &ac->ac_attr->ad_cname);
+                       if (rc) break;
+               }
+               break;
+       case LDAP_MOD_DELETE:
+               if (c->valx < 0) {
+                       for (ac = ai->ai_case; ac; ac=ai->ai_case) {
+                               ai->ai_case = ac->ac_next;
+                               ch_free(ac);
+                       }
+               } else {
+                       int i;
+                       for (i=0, a2 = &ai->ai_case; i<c->valx; i++, a2 = &(*a2)->ac_next);
+                       ac = *a2;
+                       *a2 = ac->ac_next;
+                       ch_free(ac);
+               }
+               rc = 0;
+               break;
+       default: {
+               const char *text;
+               adremap_case ad;
+               ad.ac_attr = NULL;
+               rc = slap_str2ad(c->argv[1], &ad.ac_attr, &text);
+               if (rc) break;
+               for (a2 = &ai->ai_case; *a2; a2 = &(*a2)->ac_next);
+               ac = ch_malloc(sizeof(adremap_case));
+               ac->ac_next = NULL;
+               ac->ac_attr = ad.ac_attr;
+               *a2 = ac;
+               break;
+               }
+       }
+       return rc;
+}
+
+static int
+adremap_cf_dnv(ConfigArgs *c)
+{
+       BackendDB *be = (BackendDB *)c->be;
+       slap_overinst *on = (slap_overinst *)c->bi;
+       adremap_info *ai = on->on_bi.bi_private;
+       adremap_dnv *ad, **a2;
+       int rc = ARG_BAD_CONF;
+
+       switch(c->op) {
+       case SLAP_CONFIG_EMIT:
+               for (ad = ai->ai_dnv; ad; ad=ad->ad_next) {
+                       char *ptr;
+                       struct berval bv;
+                       bv.bv_len = ad->ad_dnattr->ad_cname.bv_len + ad->ad_deref->ad_cname.bv_len + ad->ad_newattr->ad_cname.bv_len + 2;
+                       bv.bv_val = ch_malloc(bv.bv_len + 1);
+                       ptr = lutil_strcopy(bv.bv_val, ad->ad_dnattr->ad_cname.bv_val);
+                       *ptr++ = ' ';
+                       ptr = lutil_strcopy(ptr, ad->ad_deref->ad_cname.bv_val);
+                       *ptr++ = ' ';
+                       strcpy(ptr, ad->ad_newattr->ad_cname.bv_val);
+                       ber_bvarray_add(&c->rvalue_vals, &bv);
+               }
+               if (ai->ai_dnv) rc = 0;
+               break;
+       case LDAP_MOD_DELETE:
+               if (c->valx < 0) {
+                       for (ad = ai->ai_dnv; ad; ad=ai->ai_dnv) {
+                               ai->ai_dnv = ad->ad_next;
+                               ch_free(ad);
+                       }
+               } else {
+                       int i;
+                       for (i=0, a2 = &ai->ai_dnv; i<c->valx; i++, a2 = &(*a2)->ad_next);
+                       ad = *a2;
+                       *a2 = ad->ad_next;
+                       ch_free(ad);
+               }
+               rc = 0;
+               break;
+       default: {
+               const char *text;
+               adremap_dnv av = {0};
+               rc = slap_str2ad(c->argv[1], &av.ad_dnattr, &text);
+               if (rc) break;
+               if (av.ad_dnattr->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName) {
+                       rc = 1;
+                       snprintf(c->cr_msg, sizeof(c->cr_msg), "<%s> not a DN-valued attribute",
+                               c->argv[0]);
+                       Debug(LDAP_DEBUG_ANY, "%s: %s(%s)\n", c->log, c->cr_msg, c->argv[1]);
+                       break;
+               }
+               rc = slap_str2ad(c->argv[2], &av.ad_deref, &text);
+               if (rc) break;
+               rc = slap_str2ad(c->argv[3], &av.ad_newattr, &text);
+               if (rc) break;
+
+               for (a2 = &ai->ai_dnv; *a2; a2 = &(*a2)->ad_next);
+               ad = ch_malloc(sizeof(adremap_dnv));
+               ad->ad_next = NULL;
+               ad->ad_dnattr = av.ad_dnattr;
+               ad->ad_deref = av.ad_deref;
+               ad->ad_newattr = av.ad_newattr;
+               *a2 = ad;
+               break;
+               }
+       }
+       return rc;
+}
+
+static int
+adremap_search_resp(
+       Operation *op,
+       SlapReply *rs
+)
+{
+       slap_overinst *on = op->o_callback->sc_private;
+       adremap_info *ai = on->on_bi.bi_private;
+       adremap_case *ac;
+       adremap_dnv *ad;
+       Attribute *a;
+       Entry *e;
+
+       if (rs->sr_type != REP_SEARCH)
+               return SLAP_CB_CONTINUE;
+
+       e = rs->sr_entry;
+       for (ac = ai->ai_case; ac; ac = ac->ac_next) {
+               a = attr_find(e->e_attrs, ac->ac_attr);
+               if (a) {
+                       int i, j;
+                       if (!(rs->sr_flags & REP_ENTRY_MODIFIABLE)) {
+                               e = entry_dup(e);
+                               rs_replace_entry(op, rs, on, e);
+                               rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+                               a = attr_find(e->e_attrs, ac->ac_attr);
+                       }
+                       for (i=0; i<a->a_numvals; i++) {
+                               unsigned char *c = a->a_vals[i].bv_val;
+                               for (j=0; j<a->a_vals[i].bv_len; j++)
+                                       if (isupper(c[j]))
+                                               c[j] = tolower(c[j]);
+                       }
+               }
+       }
+       for (ad = ai->ai_dnv; ad; ad = ad->ad_next) {
+               a = attr_find(e->e_attrs, ad->ad_dnattr);
+               if (a) {
+                       Entry *n;
+                       Attribute *dr;
+                       int i, rc;
+                       if (!(rs->sr_flags & REP_ENTRY_MODIFIABLE)) {
+                               e = entry_dup(e);
+                               rs_replace_entry(op, rs, on, e);
+                               rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+                               a = attr_find(e->e_attrs, ad->ad_dnattr);
+                       }
+                       for (i=0; i<a->a_numvals; i++) {
+                               n = NULL;
+                               rc = be_entry_get_rw(op, &a->a_nvals[i], NULL, ad->ad_deref, 0, &n);
+                               if (!rc && n) {
+                                       dr = attr_find(n->e_attrs, ad->ad_deref);
+                                       if (dr)
+                                               attr_merge_one(e, ad->ad_newattr, dr->a_vals, dr->a_nvals);
+                                       be_entry_release_r(op, n);
+                               }
+                       }
+               }
+       }
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+adremap_search(
+       Operation *op,
+       SlapReply *rs
+)
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       slap_callback *cb;
+
+       cb = op->o_tmpcalloc(1, sizeof(slap_callback), op->o_tmpmemctx);
+       cb->sc_response = adremap_search_resp;
+       cb->sc_private = on;
+       cb->sc_next = op->o_callback;
+       op->o_callback = cb;
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+adremap_db_init(
+       BackendDB *be,
+       ConfigReply *cr
+)
+{
+       slap_overinst *on = (slap_overinst *) be->bd_info;
+
+       /* initialize private structure to store configuration */
+       on->on_bi.bi_private = ch_calloc( 1, sizeof(adremap_info) );
+
+       return 0;
+}
+
+static int
+adremap_db_destroy(
+       BackendDB *be,
+       ConfigReply *cr
+)
+{
+       slap_overinst *on = (slap_overinst *) be->bd_info;
+       adremap_info *ai = (adremap_info *) on->on_bi.bi_private;
+       adremap_case *ac;
+       adremap_dnv *ad;
+
+       /* free config */
+       for (ac = ai->ai_case; ac; ac = ai->ai_case) {
+               ai->ai_case = ac->ac_next;
+               ch_free(ac);
+       }
+       for (ad = ai->ai_dnv; ad; ad = ai->ai_dnv) {
+               ai->ai_dnv = ad->ad_next;
+               ch_free(ad);
+       }
+       free( ai );
+
+       return 0;
+}
+
+static slap_overinst adremap;
+
+int adremap_initialize()
+{
+       int i, code;
+
+       adremap.on_bi.bi_type = "adremap";
+       adremap.on_bi.bi_db_init = adremap_db_init;
+       adremap.on_bi.bi_db_destroy = adremap_db_destroy;
+       adremap.on_bi.bi_op_search = adremap_search;
+
+       /* register configuration directives */
+       adremap.on_bi.bi_cf_ocs = adremapocs;
+       code = config_register_schema( adremapcfg, adremapocs );
+       if ( code ) return code;
+
+       return overlay_register( &adremap );
+}
+
+#if SLAPD_OVER_ADREMAP == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+       return adremap_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_ADREMAP) */
diff --git a/contrib/slapd-modules/adremap/slapo-adremap.5 b/contrib/slapd-modules/adremap/slapo-adremap.5
new file mode 100644 (file)
index 0000000..4a8d751
--- /dev/null
@@ -0,0 +1,94 @@
+.TH SLAPO-ADREMAP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015 Howard Chu, All Rights Reserved.
+.\" $OpenLDAP$
+.SH NAME
+slapo-adremap \- AD Remap overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B adremap
+overlay to
+.BR slapd (8)
+remaps some attribute values for compatibility between Microsoft AD
+and older POSIX systems' PAM/NSS clients. It can be configured to
+convert values of given attributes to lower case, and it can be
+configured to generate RFC2307-compliant group memberships based
+on RFC2307bis groups. All mapping is only performed on entries
+returned as search responses.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B adremap
+overlay must be prefixed by
+.BR adremap\- ,
+to avoid potential conflicts with directives specific to the underlying 
+database or to other stacked overlays.
+
+.TP
+.B overlay adremap
+This directive adds the
+.B adremap
+overlay to the current database, see
+.BR slapd.conf (5)
+for details.
+
+.LP
+These
+.B slapd.conf
+configuration options are defined for the adremap overlay. They must
+appear after the
+.B overlay
+directive. They can each be specified multiple times:
+.TP
+.B adremap-downcase <attr>
+Specify an attributeType whose values will all be mapped to lowercase
+when returned in search responses.
+.TP
+.B adremap-dnmap <dnattr> <derefattr> <newattr>
+Specify a DN-valued attributeType whose values will be dereferenced. The
+.B <derefattr>
+of the target entry will be retrieved and its value will be added to the
+.B <newattr>
+in the entry.
+
+.SH EXAMPLE
+This example configures the
+.B adremap
+overlay to map all
+.B uid
+attributes to lowercase, and create
+.B memberUid
+values for group entries.
+Add the following to
+.BR slapd.conf (5):
+
+.LP
+.nf
+    database <database>
+    # ...
+
+    overlay adremap
+    adremap-downcase uid
+       adremap-dnmap member uid memberUid
+.fi
+.LP
+.B slapd
+must also load
+.B adremap.la,
+if compiled as a run-time module;
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+The
+.BR slapo-adremap (5)
+overlay supports dynamic configuration via
+.BR back-config.
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2015 by Howard Chu.