diff --git a/contrib/ConfigOIDs b/contrib/ConfigOIDs index a04675a3d7..6dd4a9a4a1 100644 --- a/contrib/ConfigOIDs +++ b/contrib/ConfigOIDs @@ -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 index 0000000000..e4cab3bdbe --- /dev/null +++ b/contrib/slapd-modules/adremap/Makefile @@ -0,0 +1,56 @@ +# $OpenLDAP$ +# Copyright 2015 Howard Chu +# 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 +# . + +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 index 0000000000..38c95aafce --- /dev/null +++ b/contrib/slapd-modules/adremap/adremap.c @@ -0,0 +1,352 @@ +/* adremap.c - Case-folding and DN-value remapping for AD proxies */ +/* $OpenLDAP$ */ +/* + * Copyright 2015 Howard Chu . + * 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 + * . + */ + +#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 +#include "lutil.h" +#include "slap.h" +#include +#include +#include +#include +#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; ivalx; 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; ivalx; 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; ia_numvals; i++) { + unsigned char *c = a->a_vals[i].bv_val; + for (j=0; ja_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; ia_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 index 0000000000..4a8d7515b4 --- /dev/null +++ b/contrib/slapd-modules/adremap/slapo-adremap.5 @@ -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 +Specify an attributeType whose values will all be mapped to lowercase +when returned in search responses. +.TP +.B adremap-dnmap +Specify a DN-valued attributeType whose values will be dereferenced. The +.B +of the target entry will be retrieved and its value will be added to the +.B +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 + # ... + + 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.