From 74fa239a201cd2d785fe34bdbaf6804161bdb231 Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Sat, 12 May 2001 00:51:28 +0000 Subject: [PATCH] This is the commit of: - librewrite, for string rewriting; it may be used in back-ldap by configuring with '--enable-rewrite'. It must be used in back-meta. There's a text file, 'libraries/librewrite/RATIONALE', that explains the usage and the features. More comprehensive documentation will follow. - enhancements of back-ldap (ITS#989,ITS#998,ITS#1002,ITS#1054 and ITS#1137) including dn rewriting, a fix to group acl matching and so - back-meta: a new backend that proxies a set of remote servers by spawning queries. It uses portions of back-ldap and the rewrite capabilities of librewrite. It can be compiled by configuring with `--enable-ldap --enable-rewrite --enable-meta'. There's a text file, 'servers/slapd/back-meta/Documentation', that describes the main features and config statements. Note: someone (Kurt?) should run 'autoconf' and commit 'configure' as my autoconf version must be different: my configures contain a number of differences and I didn't feel comfortable in adding them :) --- configure.in | 50 ++ include/portable.h.in | 9 + include/rewrite.h | 222 +++++ libraries/Makefile.in | 2 +- libraries/librewrite/Copyright | 23 + libraries/librewrite/Makefile.in | 27 + libraries/librewrite/RATIONALE | 397 +++++++++ libraries/librewrite/config.c | 412 +++++++++ libraries/librewrite/context.c | 415 +++++++++ libraries/librewrite/info.c | 254 ++++++ libraries/librewrite/ldapmap.c | 333 ++++++++ libraries/librewrite/map.c | 807 ++++++++++++++++++ libraries/librewrite/params.c | 140 +++ libraries/librewrite/parse.c | 127 +++ libraries/librewrite/rewrite-int.h | 554 ++++++++++++ libraries/librewrite/rewrite-map.h | 56 ++ libraries/librewrite/rewrite.c | 147 ++++ libraries/librewrite/rule.c | 393 +++++++++ libraries/librewrite/session.c | 330 +++++++ libraries/librewrite/subst.c | 427 +++++++++ libraries/librewrite/var.c | 199 +++++ servers/slapd/back-ldap/add.c | 131 ++- servers/slapd/back-ldap/back-ldap.h | 40 +- servers/slapd/back-ldap/bind.c | 124 ++- servers/slapd/back-ldap/compare.c | 36 +- servers/slapd/back-ldap/config.c | 205 ++++- servers/slapd/back-ldap/delete.c | 31 +- servers/slapd/back-ldap/group.c | 198 +++-- servers/slapd/back-ldap/init.c | 29 +- servers/slapd/back-ldap/modify.c | 72 +- servers/slapd/back-ldap/modrdn.c | 70 +- servers/slapd/back-ldap/search.c | 249 +++++- servers/slapd/back-ldap/suffixmassage.c | 3 + servers/slapd/back-ldap/unbind.c | 11 +- servers/slapd/back-meta/Changes | 68 ++ servers/slapd/back-meta/Copyright | 63 ++ servers/slapd/back-meta/Documentation | 285 +++++++ servers/slapd/back-meta/Makefile.in | 26 + servers/slapd/back-meta/TODO | 20 + servers/slapd/back-meta/add.c | 189 ++++ servers/slapd/back-meta/attribute.c | 208 +++++ servers/slapd/back-meta/back-meta.h | 284 ++++++ servers/slapd/back-meta/bind.c | 356 ++++++++ servers/slapd/back-meta/candidates.c | 284 ++++++ servers/slapd/back-meta/compare.c | 336 ++++++++ servers/slapd/back-meta/config.c | 519 +++++++++++ servers/slapd/back-meta/conn.c | 501 +++++++++++ servers/slapd/back-meta/data/meta-1.ldif | 10 + servers/slapd/back-meta/data/meta-2.ldif | 10 + servers/slapd/back-meta/data/meta-3.ldif | 11 + servers/slapd/back-meta/data/setup.sh | 51 ++ .../slapd/back-meta/data/slapd-ldap-raw.conf | 15 + .../back-meta/data/slapd-ldap-rewrite.conf | 44 + servers/slapd/back-meta/data/slapd-ldbm.conf | 30 + .../back-meta/data/slapd-meta-plain.conf | 43 + .../back-meta/data/slapd-meta-rewrite.conf | 82 ++ servers/slapd/back-meta/delete.c | 128 +++ servers/slapd/back-meta/dncache.c | 281 ++++++ servers/slapd/back-meta/external.h | 211 +++++ servers/slapd/back-meta/group.c | 272 ++++++ servers/slapd/back-meta/init.c | 250 ++++++ servers/slapd/back-meta/modify.c | 195 +++++ servers/slapd/back-meta/modrdn.c | 187 ++++ servers/slapd/back-meta/search.c | 666 +++++++++++++++ servers/slapd/back-meta/unbind.c | 127 +++ servers/slapd/backend.c | 6 + 66 files changed, 12105 insertions(+), 176 deletions(-) create mode 100644 include/rewrite.h create mode 100644 libraries/librewrite/Copyright create mode 100644 libraries/librewrite/Makefile.in create mode 100644 libraries/librewrite/RATIONALE create mode 100644 libraries/librewrite/config.c create mode 100644 libraries/librewrite/context.c create mode 100644 libraries/librewrite/info.c create mode 100644 libraries/librewrite/ldapmap.c create mode 100644 libraries/librewrite/map.c create mode 100644 libraries/librewrite/params.c create mode 100644 libraries/librewrite/parse.c create mode 100644 libraries/librewrite/rewrite-int.h create mode 100644 libraries/librewrite/rewrite-map.h create mode 100644 libraries/librewrite/rewrite.c create mode 100644 libraries/librewrite/rule.c create mode 100644 libraries/librewrite/session.c create mode 100644 libraries/librewrite/subst.c create mode 100644 libraries/librewrite/var.c create mode 100644 servers/slapd/back-meta/Changes create mode 100644 servers/slapd/back-meta/Copyright create mode 100644 servers/slapd/back-meta/Documentation create mode 100644 servers/slapd/back-meta/Makefile.in create mode 100644 servers/slapd/back-meta/TODO create mode 100644 servers/slapd/back-meta/add.c create mode 100644 servers/slapd/back-meta/attribute.c create mode 100644 servers/slapd/back-meta/back-meta.h create mode 100644 servers/slapd/back-meta/bind.c create mode 100644 servers/slapd/back-meta/candidates.c create mode 100644 servers/slapd/back-meta/compare.c create mode 100644 servers/slapd/back-meta/config.c create mode 100644 servers/slapd/back-meta/conn.c create mode 100644 servers/slapd/back-meta/data/meta-1.ldif create mode 100644 servers/slapd/back-meta/data/meta-2.ldif create mode 100644 servers/slapd/back-meta/data/meta-3.ldif create mode 100644 servers/slapd/back-meta/data/setup.sh create mode 100644 servers/slapd/back-meta/data/slapd-ldap-raw.conf create mode 100644 servers/slapd/back-meta/data/slapd-ldap-rewrite.conf create mode 100644 servers/slapd/back-meta/data/slapd-ldbm.conf create mode 100644 servers/slapd/back-meta/data/slapd-meta-plain.conf create mode 100644 servers/slapd/back-meta/data/slapd-meta-rewrite.conf create mode 100644 servers/slapd/back-meta/delete.c create mode 100644 servers/slapd/back-meta/dncache.c create mode 100644 servers/slapd/back-meta/external.h create mode 100644 servers/slapd/back-meta/group.c create mode 100644 servers/slapd/back-meta/init.c create mode 100644 servers/slapd/back-meta/modify.c create mode 100644 servers/slapd/back-meta/modrdn.c create mode 100644 servers/slapd/back-meta/search.c create mode 100644 servers/slapd/back-meta/unbind.c diff --git a/configure.in b/configure.in index fb30f871d7..1156691d29 100644 --- a/configure.in +++ b/configure.in @@ -116,6 +116,7 @@ OL_ARG_ENABLE(referrals,[ --enable-referrals enable V2 Referrals (experimenta OL_ARG_ENABLE(kbind,[ --enable-kbind enable V2 Kerberos IV bind (deprecated)], no)dnl OL_ARG_ENABLE(ipv6,[ --enable-ipv6 enable IPv6 support], auto)dnl OL_ARG_ENABLE(local,[ --enable-local enable AF_LOCAL (AF_UNIX) socket support], auto)dnl +OL_ARG_ENABLE(rewrite,[ --enable-rewrite enable rewrite], no)dnl OL_ARG_ENABLE(x_compile,[ --enable-x-compile enable cross compiling], no, [yes no])dnl @@ -177,6 +178,9 @@ OL_ARG_WITH(ldbm_module,[ --with-ldbm-module module type], static, [static dynamic]) OL_ARG_WITH(ldbm_type,[ --with-ldbm-type use LDBM type], auto, [auto btree hash]) +OL_ARG_ENABLE(meta,[ --enable-meta enable metadirectory backend], no)dnl +OL_ARG_WITH(meta_module,[ --with-meta-module module type], static, + [static dynamic]) OL_ARG_ENABLE(passwd,[ --enable-passwd enable passwd backend], no)dnl OL_ARG_WITH(passwd_module,[ --with-passwd-module module type], static, [static dynamic]) @@ -222,6 +226,9 @@ if test $ol_enable_slapd = no ; then if test $ol_enable_ldbm = yes ; then AC_MSG_WARN([slapd disabled, ignoring --enable-ldbm argument]) fi + if test $ol_enable_meta = yes ; then + AC_MSG_WARN([slapd disabled, ignoring --enable-meta argument]) + fi if test $ol_enable_passwd = yes ; then AC_MSG_WARN([slapd disabled, ignoring --enable-passwd argument]) fi @@ -273,6 +280,9 @@ dnl fi if test $ol_with_ldbm_module != static ; then AC_MSG_WARN([slapd disabled, ignoring --with-ldbm-module argument]) fi + if test $ol_with_meta_module != static ; then + AC_MSG_WARN([slapd disabled, ignoring --with-meta-module argument]) + fi if test $ol_with_passwd_module != static ; then AC_MSG_WARN([slapd disabled, ignoring --with-passwd-module argument]) fi @@ -291,12 +301,16 @@ dnl fi if test $ol_enable_slurpd = yes ; then AC_MSG_ERROR([slurpd requires slapd]) fi + if test $ol_enable_rewrite = yes ; then + AC_MSG_WARN([slapd disabled, ignoring --enable-rewrite argument]) + fi # force settings to no ol_enable_bdb=no ol_enable_dnssrv=no ol_enable_ldap=no ol_enable_ldbm=no + ol_enable_meta=no ol_enable_passwd=no ol_enable_perl=no ol_enable_shell=no @@ -318,6 +332,7 @@ dnl ol_enable_multimaster=no ol_with_dnssrv_module=static ol_with_ldap_module=static ol_with_ldbm_module=static + ol_with_meta_module=static ol_with_passwd_module=static ol_with_perl_module=static ol_with_shell_module=static @@ -326,6 +341,8 @@ dnl ol_enable_multimaster=no ol_enable_slurpd=no + ol_enable_rewrite=no + elif test $ol_enable_ldbm = no ; then dnl SLAPD without LDBM @@ -345,6 +362,7 @@ elif test $ol_enable_ldbm = no ; then $ol_enable_bdb = no -a \ $ol_enable_dnssrv = no -a \ $ol_enable_ldap = no -a \ + $ol_enable_meta = no -a \ $ol_enable_passwd = no -a \ $ol_enable_perl = no -a \ $ol_enable_shell = no -a \ @@ -427,6 +445,7 @@ BUILD_BDB=no BUILD_DNSSRV=no BUILD_LDAP=no BUILD_LDBM=no +BUILD_META=no BUILD_PASSWD=no BUILD_PERL=no BUILD_SHELL=no @@ -437,6 +456,7 @@ BUILD_BDB_DYNAMIC=static BUILD_DNSSRV_DYNAMIC=static BUILD_LDAP_DYNAMIC=static BUILD_LDBM_DYNAMIC=static +BUILD_META_DYNAMIC=static BUILD_PASSWD_DYNAMIC=static BUILD_PERL_DYNAMIC=static BUILD_SHELL_DYNAMIC=static @@ -650,6 +670,7 @@ else ol_with_dnssrv_module=static ol_with_ldap_module=static ol_with_ldbm_module=static + ol_with_meta_module=static ol_with_passwd_module=static ol_with_perl_module=static ol_with_shell_module=static @@ -2342,6 +2363,25 @@ if test "$ol_link_ldbm" != no ; then fi fi +if test "$ol_enable_meta" != no ; then + if test "$ol_enable_ldap" = no ; then + AC_MSG_ERROR([need --enable-ldap to use --enable-meta]) + fi + if test "$ol_enable_rewrite" = no ; then + AC_MSG_ERROR([need --enable-rewrite to use --enable-meta]) + fi + AC_DEFINE(SLAPD_META,1,[define to support LDAP Metadirectory backend]) + BUILD_SLAPD=yes + BUILD_META=yes + if test "$ol_with_meta_module" != static ; then + AC_DEFINE(SLAPD_META_DYNAMIC,1, + [define to support dynamic LDAP Metadirectory backend]) + BUILD_META=mod + BUILD_META_DYNAMIC=shared + SLAPD_MODULES_LIST="$SLAPD_MODULES_LIST -dlopen \$(SLAP_DIR)back-meta/back_meta.la" + fi +fi + if test "$ol_enable_passwd" != no ; then AC_DEFINE(SLAPD_PASSWD,1,[define to support PASSWD backend]) BUILD_SLAPD=yes @@ -2412,6 +2452,12 @@ if test "$ol_enable_slurpd" != no -a "$ol_link_threads" != no -a \ BUILD_SLURPD=yes fi +if test "$ol_enable_rewrite" != no ; then + AC_DEFINE(ENABLE_REWRITE,1,[define to enable rewriting in back-ldap and back-meta]) + BUILD_REWRITE=yes + SLAPD_LIBS="$SLAPD_LIBS -lrewrite" +fi + dnl ---------------------------------------------------------------- if test "$LINK_BINS_DYNAMIC" = yes; then @@ -2442,6 +2488,7 @@ AC_SUBST(BUILD_SLAPD) AC_SUBST(BUILD_DNSSRV) AC_SUBST(BUILD_LDAP) AC_SUBST(BUILD_LDBM) + AC_SUBST(BUILD_META) AC_SUBST(BUILD_PASSWD) AC_SUBST(BUILD_PERL) AC_SUBST(BUILD_SHELL) @@ -2451,6 +2498,7 @@ AC_SUBST(BUILD_SLAPD) AC_SUBST(BUILD_DNSSRV_DYNAMIC) AC_SUBST(BUILD_LDAP_DYNAMIC) AC_SUBST(BUILD_LDBM_DYNAMIC) + AC_SUBST(BUILD_META_DYNAMIC) AC_SUBST(BUILD_PASSWD_DYNAMIC) AC_SUBST(BUILD_PERL_DYNAMIC) AC_SUBST(BUILD_SHELL_DYNAMIC) @@ -2523,12 +2571,14 @@ libraries/libldbm/Makefile:build/top.mk:libraries/libldbm/Makefile.in:build/lib. libraries/libldif/Makefile:build/top.mk:libraries/libldif/Makefile.in:build/lib.mk:build/lib-static.mk \ libraries/liblunicode/Makefile:build/top.mk:libraries/liblunicode/Makefile.in:build/lib.mk:build/lib-static.mk \ libraries/liblutil/Makefile:build/top.mk:libraries/liblutil/Makefile.in:build/lib.mk:build/lib-static.mk \ +libraries/librewrite/Makefile:build/top.mk:libraries/librewrite/Makefile.in:build/lib.mk:build/lib-static.mk \ servers/Makefile:build/top.mk:servers/Makefile.in:build/dir.mk \ servers/slapd/Makefile:build/top.mk:servers/slapd/Makefile.in:build/srv.mk \ servers/slapd/back-bdb/Makefile:build/top.mk:servers/slapd/back-bdb/Makefile.in:build/mod.mk \ servers/slapd/back-dnssrv/Makefile:build/top.mk:servers/slapd/back-dnssrv/Makefile.in:build/mod.mk \ servers/slapd/back-ldap/Makefile:build/top.mk:servers/slapd/back-ldap/Makefile.in:build/mod.mk \ servers/slapd/back-ldbm/Makefile:build/top.mk:servers/slapd/back-ldbm/Makefile.in:build/mod.mk \ +servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk \ servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk \ servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk \ servers/slapd/back-shell/Makefile:build/top.mk:servers/slapd/back-shell/Makefile.in:build/mod.mk \ diff --git a/include/portable.h.in b/include/portable.h.in index 0b1952aa2b..70f29290fc 100644 --- a/include/portable.h.in +++ b/include/portable.h.in @@ -930,6 +930,12 @@ /* define to support dynamic LDBM backend */ #undef SLAPD_LDBM_DYNAMIC +/* define to support LDAP Metadirectory backend */ +#undef SLAPD_META + +/* define to support dynamic LDAP Metadirectory backend */ +#undef SLAPD_META_DYNAMIC + /* define to support PASSWD backend */ #undef SLAPD_PASSWD @@ -960,6 +966,9 @@ /* define to support dynamic SQL backend */ #undef SLAPD_SQL_DYNAMIC +/* define to enable rewriting in back-ldap and back-meta */ +#undef ENABLE_REWRITE + /* begin of postamble */ diff --git a/include/rewrite.h b/include/rewrite.h new file mode 100644 index 0000000000..58315bc84e --- /dev/null +++ b/include/rewrite.h @@ -0,0 +1,222 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#ifndef REWRITE_H +#define REWRITE_H + +LDAP_BEGIN_DECL + +/* + * Default rewrite context + */ +#define REWRITE_DEFAULT_CONTEXT "default" + +/* + * Rewrite engine states + */ +#define REWRITE_OFF 0x0000 +#define REWRITE_ON 0x0001 +#define REWRITE_DEFAULT REWRITE_OFF + +/* + * Rewrite internal status returns + */ +#define REWRITE_SUCCESS LDAP_SUCCESS +#define REWRITE_ERR LDAP_OPERATIONS_ERROR +#define REWRITE_NO_SUCH_OBJECT LDAP_NO_SUCH_OBJECT + +/* + * Rewrite modes (input values for rewrite_info_init); determine the + * behavior in case a null or non existent context is required: + * + * REWRITE_MODE_ERR error + * REWRITE_MODE_OK no error but no rewrite + * REWRITE_MODE_COPY_INPUT a copy of the input is returned + * REWRITE_MODE_USE_DEFAULT the default context is used. + */ +#define REWRITE_MODE_ERR 0x0010 +#define REWRITE_MODE_OK 0x0011 +#define REWRITE_MODE_COPY_INPUT 0x0012 +#define REWRITE_MODE_USE_DEFAULT 0x0013 + +/* + * Rewrite status returns + * + * REWRITE_REGEXEC_OK success (result may be empty in case + * of no match) + * REWRITE_REGEXEC_ERR error (internal error, + * misconfiguration, map not working ...) + * REWRITE_REGEXEC_STOP internal use; never returned + * REWRITE_REGEXEC_UNWILLING the server should issue an 'unwilling + * to perform' error + */ +#define REWRITE_REGEXEC_OK 0x0000 +#define REWRITE_REGEXEC_ERR 0x0001 +#define REWRITE_REGEXEC_STOP 0x0002 +#define REWRITE_REGEXEC_UNWILLING 0x0004 + +/* + * Rewrite info + */ +struct rewrite_info; +struct berval; + +/* + * Inits the info + */ +extern struct rewrite_info * +rewrite_info_init( + int mode +); + +/* + * Cleans up the info structure + */ +extern int +rewrite_info_delete( + struct rewrite_info *info +); + + +/* + * Parses a config line and takes actions to fit content in rewrite structure; + * lines handled are of the form: + * + * rewriteEngine {on|off} + * rewriteMaxPasses numPasses + * rewriteContext contextName [alias aliasedRewriteContex] + * rewriteRule pattern substPattern [ruleFlags] + * rewriteMap mapType mapName [mapArgs] + * rewriteParam paramName paramValue + */ +extern int +rewrite_parse( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +); + +/* + * Rewrites a string according to context. + * If the engine is off, OK is returned, but the return string will be NULL. + * In case of 'unwilling to perform', UNWILLING is returned, and the + * return string will also be null. The same in case of error. + * Otherwise, OK is returned, and result will hold a newly allocated string + * with the rewriting. + * + * What to do in case of non-existing rewrite context is still an issue. + * Four possibilities: + * - error, + * - ok with NULL result, + * - ok with copy of string as result, + * - use the default rewrite context. + */ +extern int +rewrite( + struct rewrite_info *info, + const char *rewriteContext, + const char *string, + char **result +); + +/* + * Same as above; the cookie relates the rewrite to a session + */ +extern int +rewrite_session( + struct rewrite_info *info, + const char *rewriteContext, + const char *string, + const void *cookie, + char **result +); + +/* + * Inits a session + */ +extern struct rewrite_session * +rewrite_session_init( + struct rewrite_info *info, + const void *cookie +); + +/* + * Defines and inits a variable with session scope + */ +extern int +rewrite_session_var_set( + struct rewrite_info *info, + const void *cookie, + const char *name, + const char *value +); + +/* + * Deletes a session + */ +extern int +rewrite_session_delete( + struct rewrite_info *info, + const void *cookie +); + + +/* + * Params + */ + +/* + * Defines and inits a variable with global scope + */ +extern int +rewrite_param_set( + struct rewrite_info *info, + const char *name, + const char *value +); + +/* + * Gets a var with global scope + */ +extern int +rewrite_param_get( + struct rewrite_info *info, + const char *name, + struct berval *value +); + +/* + * Destroys the parameter tree + */ +extern int +rewrite_param_destroy( + struct rewrite_info *info +); + +LDAP_END_DECL + +#endif /* REWRITE_H */ + diff --git a/libraries/Makefile.in b/libraries/Makefile.in index 9d22aafffd..b8f9ab8aa9 100644 --- a/libraries/Makefile.in +++ b/libraries/Makefile.in @@ -6,5 +6,5 @@ SUBDIRS= liblunicode liblutil libldif \ liblber libldap libldap_r \ - libavl libldbm + libavl libldbm librewrite diff --git a/libraries/librewrite/Copyright b/libraries/librewrite/Copyright new file mode 100644 index 0000000000..64a25f5cee --- /dev/null +++ b/libraries/librewrite/Copyright @@ -0,0 +1,23 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ diff --git a/libraries/librewrite/Makefile.in b/libraries/librewrite/Makefile.in new file mode 100644 index 0000000000..5cd92cdf0b --- /dev/null +++ b/libraries/librewrite/Makefile.in @@ -0,0 +1,27 @@ +# $OpenLDAP$ +## Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved. +## COPYING RESTRICTIONS APPLY, see COPYRIGHT file +## +## LIBREWRITE +## +## Copyright 2000-2001 Pierangelo Masarati +## + +SRCS = config.c context.c info.c ldapmap.c map.c params.c rule.c \ + session.c subst.c var.c \ + parse.c rewrite.c +XSRCS = version.c +OBJS = config.o context.o info.o ldapmap.o map.o params.o rule.o \ + session.o subst.o var.o + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +LIBRARY = librewrite.a +PROGRAMS = rewrite +XLIBS = -lrewrite -lavl -llutil -lldap_r -llber +XXLIBS = $(SECURITY_LIBS) $(LDIF_LIBS) $(LUTIL_LIBS) +XXXLIBS = $(LTHREAD_LIBS) + +rewrite: $(LIBRARY) rewrite.o parse.o + $(LTLINK) -o $@ rewrite.o parse.o $(LIBS) diff --git a/libraries/librewrite/RATIONALE b/libraries/librewrite/RATIONALE new file mode 100644 index 0000000000..f059bd702d --- /dev/null +++ b/libraries/librewrite/RATIONALE @@ -0,0 +1,397 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +/* + * Description + * + * A string is rewritten according to a set of rules, called + * a `rewrite context'. + * The rules are based on Regular Expressions (POSIX regex) with + * substring matching; extensions are planned to allow basic variable + * substitution and map resolution of substrings. + * The behavior of pattern matching/substitution can be altered by a + * set of flags. + * + * The underlying concept is to build a lightweight rewrite module + * for the slapd server (initially dedicated to the back-ldap module). + * + * + * Passes + * + * An incoming string is matched agains a set of rules. Rules are made + * of a match pattern, a substitution pattern and a set of actions. + * In case of match a string rewriting is performed according to the + * substitution pattern that allows to refer to substrings matched + * in the incoming string. The actions, if any, are finally performed. + * The substitution pattern allows map resolution of substrings. + * A map is a generic object that maps a substitution pattern to a + * value. + * + * + * Pattern Matching Flags + * + * 'C' honors case in matching (default is case insensitive) + * 'R' use POSIX Basic Regular Expressions (default is Extended) + * + * + * Action Flags + * + * ':' apply the rule once only (default is recursive) + * '@' stop applying rules in case of match. + * '#' stop current operation if the rule matches, and issue an + * `unwilling to perform' error. + * 'G{n}' jump n rules back and forth (watch for loops!). Note that + * 'G{1}' is implicit in every rule. + * 'I' ignores errors in rule; this means, in case of error, e.g. + * issued by a map, the error is treated as a missed match. + * The 'unwilling to perform' is not overridden. + * + * the ordering of the flags is significant. For instance: + * + * 'IG{2}' means ignore errors and jump two lines ahead both in case + * of match and in case of error, while + * 'G{2}I' means ignore errors, but jump thwo lines ahead only in case + * of match. + * + * More flags (mainly Action Flags) will be added as needed. + * + * + * Pattern matching: + * + * see regex(7) + * + * + * String Substitution: + * + * the string substitution happens according to a substitution pattern. + * - susbtring substitution is allowed with the syntax '\d' + * where 'd' is a digit ranging 0-9 (0 is the full match). + * I see that 0-9 digit expansion is a widely accepted + * practise; however there is no technical reason to use + * such a strict limit. A syntax of the form '\{ddd}' + * should be fine if there is any need to use a higher + * number of possible submatches. + * - variable substitution will be allowed (at least when I + * figure out which kind of variable could be proficiently + * substituted) + * - map lookup will be allowed (map lookup of substring matches + * in gdbm, ldap(!), math(?) and so on maps 'a la sendmail'. + * - subroutine invocation will make it possible to rewrite a + * submatch in terms of the output of another rewriteContext + * + * Old syntax: + * + * '\' {0-9} [ '{' [ '(' ')' ] '}' ] + * + * where is the name of a built-in map, and + * are optional arguments to the map, if + * the map requires them. + * The following experimental maps have been implemented: + * + * \n{xpasswd} + * maps the n-th substring match as uid to + * the gecos field in /etc/passwd; + * + * \n{xfile(/absolute/path)} + * maps the n-th substring match + * to a 'key value' style plain text file. + * + * \n{xldap(ldap://url/with?%0?in?filter) + * maps the n-th substring match to an + * attribute retrieved by means of an LDAP + * url with substitution of %0 in the filter + * (NOT IMPL.) + * + * New scheme: + * + * - everything starting with '\' requires substitution; + * - the only obvious exception is '\\', which is left as is; + * - the basic substitution is '\d', where 'd' is a digit; + * 0 means the whole string, while 1-9 is a submatch; + * - in the outdated schema, the digit may be optionally + * followed by a '{', which means pipe the submatch into + * the map described by the string up to the following '}'; + * - the output of the map is used instead of the submatch; + * - in the new schema, a '\' followed by a '{' invokes an + * advanced substitution scheme. The pattern is: + * + * '\' '{' [{ }] '(' ')' '}' + * + * where must be a legal name for the map, i.e. + * + * ::= [a-z][a-z0-9]* (case insensitive) + * ::= '>' '|' '&' '&&' '*' '**' '$' + * + * and must be a legal substitution + * schema, with no limits on the nesting level. + * The operators are: + * > sub context invocation; must be a legal, + * already defined rewrite context name + * | external command invocation; must refer + * to a legal, already defined command name (NOT IMPL.) + * & variable assignment; defines a variable + * in the running operation structure which can be + * dereferenced later (NOT IMPL.) + * * variable dereferencing; must refer to a + * variable that is defined and assigned for the + * running operation (NOT IMPL.) + * $ parameter dereferencing; must refer to + * an existing parameter; the idea is to make + * some run-time parameters set by the system + * available to the rewrite engine, as the client + * host name, the bind dn if any, constant + * parameters initialized at config time, and so + * on (NOT IMPL.) + * + * Note: as the slapd parsing routines escape backslashes ('\'), + * a double backslash is required inside substitution patterns. + * To overcome the resulting heavy notation, the substitution escaping + * has been delegated to the '%' symbol, which should be used + * instead of '\' in string substitution patterns. The symbol can + * be altered at will by redefining the related macro in "rewrite-int.h". + * In the current snapshot, all the '\' on the left side of each rule + * (the regex pattern) must be converted in '\\'; all the '\' on the + * right side of the rule (the substitution pattern) must be turned + * into '%'. In the following examples, the original (more readable) + * syntax is used; however, in the servers/slapd/back-ldap/slapd.conf + * example file, the working syntax is used. + * + * + * + * Rewrite context: + * + * a rewrite context is a set of rules which are applied in sequence. + * The basic idea is to have an application initialize a rewrite + * engine (think of Apache's mod_rewrite ...) with a set of rewrite + * contexts; when string rewriting is required, one invokes the + * appropriate rewrite context with the input string and obtains the + * newly rewritten one if no errors occur. + * + * An interesting application, in back-ldap or in slapd itself, + * could associate each basic server operation to a rewrite context + * (most of them possibly aliasing the default one). Then, DN rewriting + + could take place at any invocation of a backend operation. + * + * client -> server: + * default if defined and no specific context is available + * bindDn bind + * searchBase search + * searchFilter search + * compareDn compare + * addDn add + * modifyDn modify + * modrDn modrdn + * newSuperiorDn modrdn + * deleteDn delete + * + * server -> client: + * searchResult search (only if defined; no default) + * + * + * Configuration syntax: + * + * Basics: + * + * rewriteEngine { on | off } + * + * rewriteContext [ alias ] + * + * rewriteRule [ ] + * + * + * Additional: + * + * rewriteMap [ ] + * + * rewriteParam + * + * rewriteMaxPasses + * + * + * + * rewriteEngine: + * + * if 'on', the requested rewriting is performed; if 'off', no + * rewriting takes place (an easy way to stop rewriting without + * altering too much the configuration file) + * + * rewriteContext: + * + * is the name that identifies the context, i.e. + * the name used by the application to refer to the set of rules + * it contains. It is used also to reference sub contexts in + * string rewriting. A context may aliase another one. In this + * case the alias context contains no rule, and any reference to + * it will result in accessing the aliased one. + * + * rewriteRule: + * + * determines how a tring can be rewritten if a pattern is matched. + * Examples are reported below. + * + * rewriteMap: + * + * allows to define a map that transforms substring rewriting into + * something else. The map is referenced inside the substitution + * pattern of a rule. + * + * rewriteParam: + * + * sets a value with global scope, that can be dereferenced by the + * command '\{$paramName}'. + * + * rewriteMaxPasses: + * + * sets the maximum number of total rewriting passes taht can be + * performed in a signle rewriting operation (to avoid loops). + * + * + * Configuration examples: + * + * # set to 'off' to disable rewriting + * + * rewriteEngine on + * + * + * # everything defined here goes into the 'default' context + * # this rule changes the naming context of anything sent to + * # 'dc=home,dc=net' to 'dc=OpenLDAP, dc=org' + * + * rewriteRule "(.*)dc=home,[ ]?dc=net" "\1dc=OpenLDAP, dc=org" ":" + * + * + * # start a new context (ends input of the previous one) + * # this rule adds blancs between dn parts if not present. + * + * rewriteContext addBlancs + * rewriteRule "(.*),([^ ].*)" "\1, \2" + * + * + * # this one eats blancs + * + * rewriteContext eatBlancs + * rewriteRule "(.*),[ ](.*)" "\1,\2" + * + * + * # here control goes back to the default rewrite context; rules are + * # appended to the existing ones. + * # anything that gets here is piped into rule 'addBlancs' + * + * rewriteContext default + * rewriteRule ".*" "\{>addBlancs(\0)}" ":" + * + * + * # anything with 'uid=username' gets looked up in /etc/passwd for + * # gecos (I know it's nearly useless, but it is there just to + * # test something fancy!). Note the 'I' flag that leaves + * # 'uid=username' in place if 'username' does not have a valid + * # account. Note also the ':' that forces the rule to be processed + * # exactly once. + * + * rewriteContext uid2Gecos + * rewriteRule "(.*)uid=([a-z0-9]+),(.+)" "\1cn=\2{xpasswd},\3" "I:" + * + * + * # finally, in case of bind, if one uses a 'uid=username' dn, + * # it is rewritten in 'cn=name surname' if possible. + * + * rewriteContext bindDn + * rewriteRule ".*" "\{>addBlancs(\{>uid2Gecos(\0)})}" ":" + * + * + * # the search base is rewritten according to 'default' rules + * + * rewriteContext searchBase alias default + * + * + * # search results with OpenLDAP dn are rewritten back with + * # 'dc=home,dc=net' naming context, with spaces eaten. + * + * rewriteContext searchResult + * rewriteRule "(.*[^ ]?)[ ]?dc=OpenLDAP,[ ]?dc=org" + * "\{>eatBlancs(\1)}dc=home,dc=net" ":" + * + * # bind with email instead of full dn: we first need an ldap map + * # that turns attributes into a dn (the filter is provided by the + * # substitution string): + * + * rewriteMap ldap attr2dn "ldap://host/dc=my,dc=org?dn?sub" + * + * # then we need to detect emails; note that the rule in case of match + * # stops rewriting; in case of error, it is ignored. + * # In case we are mapping virtual to real naming contexts, we also + * # need to rewrite regular dns, because the definition of a bindDn + * # rewrite context overrides the default definition. + * + * rewriteContext bindDn + * rewriteRule "(mail=[^,]+@[^,]+)" "\{attr2dn(\1)}" "@I" + * + * # This is a rather sophisticate example. It massages a search filter + * # in case who performs the search has administrative privileges. + * # First we need to keep track of the bind dn of the incoming request: + * + * rewriteContext bindDn + * rewriteRule ".+" "\{**&binddn(\0)}" ":" + * + * # a search filter containing 'uid=' is rewritten only if an + * # appropriate dn is bound. + * # to do this, in the first rule the bound dn is dereferenced, while + * # the filter is decomposed in a prefix, the argument of the 'uid=', + * # and in a suffix. A tag '<>' is appended to the dn. If the dn + * # refers to an entry in the 'ou=admin' subtree, the filter is + * # rewritten OR-ing the 'uid=' with 'cn='; otherwise + * # it is left as is. This could be useful, for instance, to allow + * # apache's auth_ldap-1.4 module to authenticate users with both + * # 'uid' and 'cn', but only if the request comes from a possible + * # 'dn: cn=Web auth, ou=admin, dc=home, dc=net' user. + * + * rewriteContext searchFilter + * rewriteRule "(.*\()uid=([a-z0-9_]+)(\).*)" + * "\{**binddn}<>\{&prefix(\1)}\{&arg(\2)}\{&suffix(\3)}" ":I" + * rewriteRule "[^,]+,[ ]?ou=admin,[ ]?dc=home,[ ]?dc=net" + * "\{*prefix}|(uid=\{*arg})(cn=\{*arg})\{*suffix}" "@I" + * rewriteRule ".*<>" "\{*prefix}uid=\{*arg}\{*suffix}" + * + * + * LDAP Proxy resolution (a possible evolution of the back-ldap): + * + * in case the rewritten dn is an LDAP URL, the operation is initiated + * towards the host[:port] indicated in the url, if it does not refer + * to the local server. + * + * e.g.: + * + * rewriteRule '^cn=root,.*' '\0' 'G{3}' + * rewriteRule '^cn=[a-l].*' 'ldap://ldap1.my.org/\0' '@' + * rewriteRule '^cn=[m-z].*' 'ldap://ldap2.my.org/\0' '@' + * rewriteRule '.*' 'ldap://ldap3.my.org/\0' '@' + * + * (rule 1 is simply there to illustrate the 'G{n}' action; it could + * have been written: + * + * rewriteRule '^cn=root,.*' 'ldap://ldap3.my.org/\0' '@' + * + * with the advantage of saving one rewrite pass ...) + */ + diff --git a/libraries/librewrite/config.c b/libraries/librewrite/config.c new file mode 100644 index 0000000000..437d475cd9 --- /dev/null +++ b/libraries/librewrite/config.c @@ -0,0 +1,412 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" +#include "rewrite-map.h" + +/* + * Global data + */ +extern struct rewrite_context *__curr_context; + +/* + * Parses a plugin map + */ +static int +rewrite_parse_builtin_map( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +); + +/* + * Parses a config line and takes actions to fit content in rewrite structure; + * lines handled are of the form: + * + * rewriteEngine {on|off} + * rewriteMaxPasses numPasses + * rewriteContext contextName [alias aliasedContextName] + * rewriteRule pattern substPattern [ruleFlags] + * rewriteMap mapType mapName [mapArgs] + * rewriteParam paramName paramValue + */ +int +rewrite_parse( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +) +{ + int rc = -1; + + assert( info != NULL ); + assert( fname != NULL ); + assert( argv != NULL ); + assert( argc > 0 ); + + /* + * Switch on the rewrite engine + */ + if ( strcasecmp( argv[ 0 ], "rewriteEngine" ) == 0 ) { + if ( argc < 2 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteEngine needs 'state'\n%s", + fname, lineno, "" ); + return -1; + } else if ( argc > 2 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] extra fields in rewriteEngine" + " will be discarded\n%s", + fname, lineno, "" ); + } + + if ( strcasecmp( argv[ 1 ], "on" ) == 0 ) { + info->li_state = REWRITE_ON; + } else if ( strcasecmp( argv[ 1 ], "off" ) == 0 ) { + info->li_state = REWRITE_OFF; + } else { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unknown 'state' in rewriteEngine;" + " assuming 'on'\n%s", + fname, lineno, "" ); + info->li_state = REWRITE_ON; + } + rc = REWRITE_SUCCESS; + + /* + * Alter max passes + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteMaxPasses" ) == 0 ) { + if ( argc < 2 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteMaxPasses needs 'value'\n%s", + fname, lineno, "" ); + return -1; + } + info->li_max_passes = atoi( argv[ 1 ] ); + rc = REWRITE_SUCCESS; + + /* + * Start a new rewrite context and set current context + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteContext" ) == 0 ) { + if ( argc < 2 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteContext needs 'name'\n%s", + fname, lineno, "" ); + return -1; + } + + /* + * Checks for existence (lots of contexts should be + * available by default ...) + */ + __curr_context = rewrite_context_find( info, argv[ 1 ] ); + if ( __curr_context == NULL ) { + __curr_context = rewrite_context_create( info, + argv[ 1 ] ); + } + if ( __curr_context == NULL ) { + return -1; + } + + if ( argc > 2 ) { + + /* + * A context can alias another (e.g., the `builtin' + * contexts for backend operations, if not defined, + * alias the `default' rewrite context (with the + * notable exception of the searchResult context, + * which can be undefined) + */ + if ( strcasecmp( argv[ 2 ], "alias" ) == 0 ) { + struct rewrite_context *aliased; + + if ( argc == 3 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteContext" + " needs 'name' after" + " 'alias'\n%s", + fname, lineno, "" ); + return -1; + } else if ( argc > 4 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] extra fields in" + " rewriteContext" + " after aliased name" + " will be" + " discarded\n%s", + fname, lineno, "" ); + } + + aliased = rewrite_context_find( info, + argv[ 3 ] ); + if ( aliased == NULL ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] aliased" + " rewriteContext '%s'" + " does not exists\n", + fname, lineno, + argv[ 3 ] ); + return -1; + } + + __curr_context->lc_alias = aliased; + __curr_context = aliased; + } else { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] extra fields" + " in rewriteContext" + " will be discarded\n%s", + fname, lineno, "" ); + } + } + rc = REWRITE_SUCCESS; + + /* + * Compile a rule in current context + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteRule" ) == 0 ) { + if ( argc < 3 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteRule needs 'pattern'" + " 'subst' ['flags']\n%s", + fname, lineno, "" ); + return -1; + } else if ( argc > 4 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] extra fields in rewriteRule" + " will be discarded\n%s", + fname, lineno, "" ); + } + + if ( __curr_context == NULL ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteRule outside a" + " context; will add to default\n%s", + fname, lineno, "" ); + __curr_context = rewrite_context_find( info, + REWRITE_DEFAULT_CONTEXT ); + + /* + * Default context MUST exist in a properly initialized + * struct rewrite_info + */ + assert( __curr_context != NULL ); + } + + rc = rewrite_rule_compile( info, __curr_context, argv[ 1 ], + argv[ 2 ], ( argc == 4 ? argv[ 3 ] : "" ) ); + + /* + * Add a plugin map to the map tree + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 ) { + if ( argc < 3 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteMap needs at least 'type'" + " and 'name' ['args']\n%s", + fname, lineno, "" ); + return -1; + } + + rc = rewrite_parse_builtin_map( info, fname, lineno, + argc, argv ); + + /* + * Set the value of a global scope parameter + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteParam" ) == 0 ) { + if ( argc < 3 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteParam needs 'name'" + " and 'value'\n%s", + fname, lineno, "" ); + return -1; + } + + rc = rewrite_param_set( info, argv[ 1 ], argv[ 2 ] ); + + /* + * Error + */ + } else { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unknown command '%s'\n", + fname, lineno, "" ); + return -1; + } + + return rc; +} + +/* + * Compares two maps + */ +static int +rewrite_builtin_map_cmp( + const void *c1, + const void *c2 +) +{ + const struct rewrite_builtin_map *m1, *m2; + + m1 = ( struct rewrite_builtin_map * )c1; + m2 = ( struct rewrite_builtin_map * )c2; + + assert( m1 != NULL ); + assert( m2 != NULL ); + assert( m1->lb_name != NULL ); + assert( m2->lb_name != NULL ); + + return strcasecmp( m1->lb_name, m2->lb_name ); +} + +/* + * Duplicate map ? + */ +static int +rewrite_builtin_map_dup( + void *c1, + void *c2 +) +{ + struct rewrite_builtin_map *m1, *m2; + + m1 = ( struct rewrite_builtin_map * )c1; + m2 = ( struct rewrite_builtin_map * )c2; + + assert( m1 != NULL ); + assert( m2 != NULL ); + assert( m1->lb_name != NULL ); + assert( m2->lb_name != NULL ); + + return ( strcasecmp( m1->lb_name, m2->lb_name ) == 0 ? -1 : 0 ); +} + +/* + * Adds a map to the info map tree + */ +static int +rewrite_builtin_map_insert( + struct rewrite_info *info, + struct rewrite_builtin_map *map +) +{ + /* + * May need a mutex? + */ + return avl_insert( &info->li_maps, ( caddr_t )map, + rewrite_builtin_map_cmp, + rewrite_builtin_map_dup ); +} + +/* + * Retrieves a map + */ +struct rewrite_builtin_map * +rewrite_builtin_map_find( + struct rewrite_info *info, + const char *name +) +{ + struct rewrite_builtin_map tmp; + + assert( info != NULL ); + assert( name != NULL ); + + tmp.lb_name = ( char * )name; + + return ( struct rewrite_builtin_map * )avl_find( info->li_maps, + ( caddr_t )&tmp, rewrite_builtin_map_cmp ); +} + +/* + * Parses a plugin map + */ +static int +rewrite_parse_builtin_map( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +) +{ + struct rewrite_builtin_map *map; + +#define MAP_TYPE 1 +#define MAP_NAME 2 + + assert( info != NULL ); + assert( fname != NULL ); + assert( argc > 2 ); + assert( argv != NULL ); + assert( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 ); + + map = calloc( sizeof( struct rewrite_builtin_map ), 1 ); + if ( map == NULL ) { + return REWRITE_ERR; + } + + map->lb_name = strdup( argv[ MAP_NAME ] ); + if ( map->lb_name == NULL ) { + free( map ); + return REWRITE_ERR; + } + + /* + * Built-in ldap map + */ + if ( strcasecmp( argv[ MAP_TYPE ], "ldap" ) == 0 ) { + map->lb_type = REWRITE_BUILTIN_MAP_LDAP; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( & map->lb_mutex ) ) { + free( map->lb_name ); + free( map ); + return REWRITE_ERR; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + map->lb_private = map_ldap_parse( info, fname, lineno, + argc - 3, argv + 3 ); + + /* + * Error + */ + } else { + Debug( LDAP_DEBUG_ANY, "[%s:%d] unknown map type\n%s", + fname, lineno, "" ); + return -1; + } + + return rewrite_builtin_map_insert( info, map ); +} diff --git a/libraries/librewrite/context.c b/libraries/librewrite/context.c new file mode 100644 index 0000000000..4e527e875a --- /dev/null +++ b/libraries/librewrite/context.c @@ -0,0 +1,415 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" + +/* + * Compares two struct rewrite_context based on the name; + * used by avl stuff + */ +static int +rewrite_context_cmp( + const void *c1, + const void *c2 +) +{ + struct rewrite_context *lc1, *lc2; + + lc1 = (struct rewrite_context *)c1; + lc2 = (struct rewrite_context *)c2; + + assert( c1 != NULL ); + assert( c2 != NULL ); + assert( lc1->lc_name != NULL ); + assert( lc2->lc_name != NULL ); + + return strcasecmp( lc1->lc_name, lc2->lc_name ); +} + +/* + * Returns -1 in case a duplicate struct rewrite_context + * has been inserted; used by avl stuff + */ +static int +rewrite_context_dup( + void *c1, + void *c2 + ) +{ + struct rewrite_context *lc1, *lc2; + + lc1 = (struct rewrite_context *)c1; + lc2 = (struct rewrite_context *)c2; + + assert( c1 != NULL ); + assert( c2 != NULL ); + assert( lc1->lc_name != NULL ); + assert( lc2->lc_name != NULL ); + + return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 ); +} + +/* + * Finds the context named rewriteContext in the context tree + */ +struct rewrite_context * +rewrite_context_find( + struct rewrite_info *info, + const char *rewriteContext +) +{ + struct rewrite_context *context, c; + + assert( info != NULL ); + assert( rewriteContext != NULL ); + + /* + * Fetches the required rewrite context + */ + c.lc_name = (char *)rewriteContext; + context = (struct rewrite_context *)avl_find( info->li_context, + (caddr_t)&c, rewrite_context_cmp ); + if ( context == NULL ) { + return NULL; + } + + /* + * De-aliases the context if required + */ + if ( context->lc_alias ) { + return context->lc_alias; + } + + return context; +} + +/* + * Creates a new context called rewriteContext and stores in into the tree + */ +struct rewrite_context * +rewrite_context_create( + struct rewrite_info *info, + const char *rewriteContext +) +{ + struct rewrite_context *context; + int rc; + + assert( info != NULL ); + assert( rewriteContext != NULL ); + + context = calloc( sizeof( struct rewrite_context ), 1 ); + if ( context == NULL ) { + return NULL; + } + + /* + * Context name + */ + context->lc_name = strdup( rewriteContext ); + if ( context->lc_name == NULL ) { + free( context ); + return NULL; + } + + /* + * The first, empty rule + */ + context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 ); + if ( context->lc_rule == NULL ) { + free( context->lc_name ); + free( context ); + return NULL; + } + + /* + * Add context to tree + */ + rc = avl_insert( &info->li_context, (caddr_t)context, + rewrite_context_cmp, rewrite_context_dup ); + if ( rc == -1 ) { + free( context->lc_rule ); + free( context->lc_name ); + free( context ); + return NULL; + } + + return context; +} + +/* + * Finds the next rule according to a goto action statement, + * or null in case of error. + * Helper for rewrite_context_apply. + */ +static struct rewrite_rule * +rewrite_action_goto( + struct rewrite_action *action, + struct rewrite_rule *rule +) +{ + int n; + + assert( action != NULL ); + assert( action->la_args != NULL ); + assert( rule != NULL ); + + n = ((int *)action->la_args)[ 0 ]; + + if ( n > 0 ) { + for ( ; n > 1 && rule != NULL ; n-- ) { + rule = rule->lr_next; + } + } else if ( n <= 0 ) { + for ( ; n < 1 && rule != NULL ; n++ ) { + rule = rule->lr_prev; + } + } + + return rule; +} + +/* + * Rewrites string according to context; may return: + * OK: fine; if *result != NULL rule matched and rewrite succeeded. + * STOP: fine, rule matched; stop processing following rules + * UNWILL: rule matched; force 'unwilling to perform' + */ +int +rewrite_context_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_context *context, + const char *string, + char **result +) +{ + struct rewrite_rule *rule; + char *s, *res = NULL; + int return_code = REWRITE_REGEXEC_OK; + + assert( info != NULL ); + assert( op != NULL ); + assert( context != NULL ); + assert( context->lc_rule != NULL ); + assert( string != NULL ); + assert( result != NULL ); + + op->lo_depth++; + assert( op->lo_depth > 0 ); + + Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply" + " [depth=%d] string='%s'\n%s", + op->lo_depth, string, "" ); + + s = strdup( string ); + + for ( rule = context->lc_rule->lr_next; + rule != NULL && op->lo_num_passes < info->li_max_passes; + rule = rule->lr_next, op->lo_num_passes++ ) { + int rc; + + /* + * Apply a single rule + */ + rc = rewrite_rule_apply( info, op, rule, s, &res ); + + /* + * A rule may return: + * OK with result != NULL if matched + * ERR if anything was wrong + * UNWILLING if the server should drop the request + * the latter case in honored immediately; + * the other two may require some special actions to take + * place. + */ + switch ( rc ) { + + case REWRITE_REGEXEC_ERR: + Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply" + " error ...\n%s%s%s", "", "", ""); + + /* + * Checks for special actions to be taken + * in case of error ... + */ + if ( rule->lr_action != NULL ) { + struct rewrite_action *action; + int do_continue = 0; + + for ( action = rule->lr_action; + action != NULL; + action = action->la_next ) { + switch ( action->la_type ) { + + /* + * This action takes precedence + * over the others in case of failure + */ + case REWRITE_ACTION_IGNORE_ERR: + Debug( LDAP_DEBUG_ANY, + "==> rewrite_context_apply" + " ignoring error ...\n%s%s%s", + "", "", "" ); + do_continue = 1; + break; + + /* + * Goto is honored only if it comes + * after ignore error + */ + case REWRITE_ACTION_GOTO: + if ( do_continue ) { + rule = rewrite_action_goto( action, rule ); + if ( rule == NULL ) { + return_code = REWRITE_REGEXEC_ERR; + goto rc_end_of_context; + } + } + break; + + /* + * Other actions are ignored + */ + default: + break; + } + } + + if ( do_continue ) { + if ( rule->lr_next == NULL ) { + res = s; + } + goto rc_continue; + } + } + + /* + * Default behavior is to bail out ... + */ + return_code = REWRITE_REGEXEC_ERR; + goto rc_end_of_context; + + /* + * OK means there were no errors or special return codes; + * if res is defined, it means the rule matched and we + * got a sucessful rewriting + */ + case REWRITE_REGEXEC_OK: + + /* + * It matched! Check for actions ... + */ + if ( res != NULL ) { + struct rewrite_action *action; + + free( s ); + s = res; + + for ( action = rule->lr_action; + action != NULL; + action = action->la_next ) { + + switch ( action->la_type ) { + + /* + * This ends the rewrite context + * successfully + */ + case REWRITE_ACTION_STOP: + goto rc_end_of_context; + + /* + * This instructs the server to return + * an `unwilling to perform' error + * message + */ + case REWRITE_ACTION_UNWILLING: + return_code = REWRITE_REGEXEC_UNWILLING; + goto rc_end_of_context; + + /* + * This causes the processing to + * jump n rules back and forth + */ + case REWRITE_ACTION_GOTO: + rule = rewrite_action_goto( action, rule ); + if ( rule == NULL ) { + return_code = REWRITE_REGEXEC_ERR; + goto rc_end_of_context; + } + break; + + default: + /* ... */ + break; + } + } + + /* + * If result was OK and string didn't match, + * in case of last rule we need to set the + * result back to the string + */ + } else if ( rule->lr_next == NULL ) { + res = s; + } + + break; + + /* + * A STOP has propagated ... + */ + case REWRITE_REGEXEC_STOP: + goto rc_end_of_context; + + /* + * This will instruct the server to return + * an `unwilling to perform' error message + */ + case REWRITE_REGEXEC_UNWILLING: + return_code = REWRITE_REGEXEC_UNWILLING; + goto rc_end_of_context; + + } + +rc_continue: /* sent here by actions that require to continue */ + + } + +rc_end_of_context: + *result = res; + + Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply" + " [depth=%d] res={%d,'%s'}\n", + op->lo_depth, return_code, ( res ? res : "NULL" ) ); + + assert( op->lo_depth > 0 ); + op->lo_depth--; + + return return_code; +} + diff --git a/libraries/librewrite/info.c b/libraries/librewrite/info.c new file mode 100644 index 0000000000..2b8184e15d --- /dev/null +++ b/libraries/librewrite/info.c @@ -0,0 +1,254 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" + +/* + * Global data + */ + +/* + * This becomes the running context for subsequent calls to + * rewrite_parse; it can be altered only by a + * rewriteContext config line or by a change in info. + */ +struct rewrite_context *__curr_context = NULL; + +/* + * Inits the info + */ +struct rewrite_info * +rewrite_info_init( + int mode +) +{ + struct rewrite_info *info; + struct rewrite_context *context; + + switch ( mode ) { + case REWRITE_MODE_ERR: + case REWRITE_MODE_OK: + case REWRITE_MODE_COPY_INPUT: + case REWRITE_MODE_USE_DEFAULT: + break; + default: + mode = REWRITE_MODE_USE_DEFAULT; + break; + /* return NULL */ + } + + /* + * Resets the running context for parsing ... + */ + __curr_context = NULL; + + info = calloc( sizeof( struct rewrite_info ), 1 ); + if ( info == NULL ) { + return NULL; + } + + info->li_state = REWRITE_DEFAULT; + info->li_max_passes = REWRITE_MAX_PASSES; + info->li_rewrite_mode = mode; + + /* + * Add the default (empty) rule + */ + context = rewrite_context_create( info, REWRITE_DEFAULT_CONTEXT ); + if ( context == NULL ) { + free( info ); + return NULL; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_rdwr_init( &info->li_cookies_mutex ) ) { + free( info ); + return NULL; + } + if ( ldap_pvt_thread_rdwr_init( &info->li_params_mutex ) ) { + free( info ); + return NULL; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return info; +} + +/* + * Cleans up the info structure + */ +int +rewrite_info_delete( + struct rewrite_info *info +) +{ + assert( info != NULL ); + + rewrite_session_destroy( info ); + + rewrite_param_destroy( info ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_destroy( &info->li_cookies_mutex ); + ldap_pvt_thread_rdwr_destroy( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + +/* + * Rewrites a string according to context. + * If the engine is off, OK is returned, but the return string will be NULL. + * In case of 'unwilling to perform', UNWILLING is returned, and the + * return string will also be null. The same in case of error. + * Otherwise, OK is returned, and result will hold a newly allocated string + * with the rewriting. + * + * What to do in case of non-existing rewrite context is still an issue. + * Four possibilities: + * - error, + * - ok with NULL result, + * - ok with copy of string as result, + * - use the default rewrite context. + */ +int +rewrite( + struct rewrite_info *info, + const char *rewriteContext, + const char *string, + char **result +) +{ + return rewrite_session( info, rewriteContext, + string, NULL, result ); +} + +int +rewrite_session( + struct rewrite_info *info, + const char *rewriteContext, + const char *string, + const void *cookie, + char **result +) +{ + struct rewrite_context *context; + struct rewrite_op op = { 0, 0, NULL, NULL, NULL, NULL }; + int rc; + + assert( info != NULL ); + assert( rewriteContext != NULL ); + assert( string != NULL ); + assert( result != NULL ); + + /* + * cookie can be null; means: don't care about session stuff + */ + + *result = NULL; + op.lo_cookie = cookie; + + /* + * Engine not on means no failure, but explicit no rewriting + */ + if ( info->li_state != REWRITE_ON ) { + rc = REWRITE_REGEXEC_OK; + goto rc_return; + } + + /* + * Undefined context means no rewriting also + * (conservative, are we sure it's what we want?) + */ + context = rewrite_context_find( info, rewriteContext ); + if ( context == NULL ) { + switch ( info->li_rewrite_mode ) { + case REWRITE_MODE_ERR: + rc = REWRITE_REGEXEC_ERR; + goto rc_return; + case REWRITE_MODE_OK: + rc = REWRITE_REGEXEC_OK; + goto rc_return; + case REWRITE_MODE_COPY_INPUT: + *result = strdup( string ); + rc = REWRITE_REGEXEC_OK; + goto rc_return; + case REWRITE_MODE_USE_DEFAULT: + context = rewrite_context_find( info, + REWRITE_DEFAULT_CONTEXT ); + break; + } + } + + op.lo_string = strdup( string ); + if ( op.lo_string == NULL ) { + rc = REWRITE_REGEXEC_ERR; + goto rc_return; + } + + /* + * Applies rewrite context + */ + rc = rewrite_context_apply(info, &op, context, string, result ); + assert( op.lo_depth == 0 ); + + /* ?!? */ + free( op.lo_string ); + + switch ( rc ) { + /* + * Success + */ + case REWRITE_REGEXEC_OK: + case REWRITE_REGEXEC_STOP: + /* + * If rewrite succeeded return OK regardless of how + * the successful rewriting was obtained! + */ + rc = REWRITE_REGEXEC_OK; + break; + + + /* + * Internal or forced error, return = NULL; rc already OK. + */ + case REWRITE_REGEXEC_UNWILLING: + case REWRITE_REGEXEC_ERR: + default: + if ( *result != NULL ) { + free( *result ); + *result = NULL; + } + } + +rc_return: + if ( op.lo_vars ) { + rewrite_var_delete( op.lo_vars ); + } + + return rc; +} + diff --git a/libraries/librewrite/ldapmap.c b/libraries/librewrite/ldapmap.c new file mode 100644 index 0000000000..16db729a0d --- /dev/null +++ b/libraries/librewrite/ldapmap.c @@ -0,0 +1,333 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" +#include "rewrite-map.h" + +/* + * LDAP map data structure + */ +struct ldap_map_data { + char *url; + LDAPURLDesc *lud; + int attrsonly; + char *binddn; + char *bindpw; + +#define MAP_LDAP_EVERYTIME 0x00 +#define MAP_LDAP_NOW 0x01 +#define MAP_LDAP_LATER 0x02 + int when; + + LDAP *ld; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_t mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ +}; + +static void +map_ldap_free( + struct ldap_map_data *data +) +{ + assert( data != NULL ); + + if ( data->url != NULL ) { + free( data->url ); + } + + if ( data->lud != NULL ) { + ldap_free_urldesc( data->lud ); + } + + if ( data->binddn != NULL ) { + free( data->binddn ); + } + + if ( data->bindpw != NULL ) { + free( data->bindpw ); + } + + if ( data->when != MAP_LDAP_EVERYTIME && data->ld != NULL ) { + ldap_unbind_s( data->ld ); + } + + free( data ); +} + +void * +map_ldap_parse( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +) +{ + struct ldap_map_data *data; + char *p; + + assert( info != NULL ); + assert( fname != NULL ); + assert( argv != NULL ); + + data = calloc( sizeof( struct ldap_map_data ), 1 ); + if ( data == NULL ) { + return NULL; + } + + if ( argc < 1 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] ldap map needs URI\n%s", + fname, lineno, "" ); + free( data ); + return NULL; + } + + data->url = strdup( argv[ 0 ] ); + if ( data->url == NULL ) { + map_ldap_free( data ); + return NULL; + } + + if ( ldap_url_parse( argv[ 0 ], &data->lud ) != REWRITE_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] illegal URI '%s'\n", + fname, lineno, argv[ 0 ] ); + map_ldap_free( data ); + return NULL; + } + + p = strchr( data->url, '/' ); + assert( p[ 1 ] == '/' ); + if ( ( p = strchr( p + 2, '/' ) ) != NULL ) { + p[ 0 ] = '\0'; + } + + if ( strcasecmp( data->lud->lud_attrs[ 0 ], "dn" ) == 0 ) { + data->attrsonly = 1; + } + + for ( argc--, argv++; argc > 0; argc--, argv++ ) { + if ( strncasecmp( argv[ 0 ], "binddn=", 7 ) == 0 ) { + char *p = argv[ 0 ] + 7; + int l; + + if ( p[ 0 ] == '\"' || p [ 0 ] == '\'' ) { + l = strlen( p ) - 2; + p++; + if ( p[ l ] != p[ 0 ] ) { + map_ldap_free( data ); + return NULL; + } + } else { + l = strlen( p ); + } + + data->binddn = strdup( p ); + if ( data->binddn == NULL ) { + map_ldap_free( data ); + return NULL; + } + + if ( data->binddn[ l ] == '\"' + || data->binddn[ l ] == '\'' ) { + data->binddn[ l ] = '\0'; + } + } else if ( strncasecmp( argv[ 0 ], "bindpw=", 7 ) == 0 ) { + data->bindpw = strdup( argv[ 2 ] + 7 ); + if ( data->bindpw == NULL ) { + map_ldap_free( data ); + return NULL; + } + } else if ( strncasecmp( argv[ 0 ], "bindwhen=", 9 ) == 0 ) { + char *p = argv[ 0 ] + 9; + + if ( strcasecmp( p, "now" ) == 0 ) { + int rc; + + data->when = MAP_LDAP_NOW; + + /* + * Init LDAP handler ... + */ + rc = ldap_initialize( &data->ld, data->url ); + if ( rc != LDAP_SUCCESS ) { + map_ldap_free( data ); + return NULL; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_init( &data->mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + } else if ( strcasecmp( p, "later" ) == 0 ) { + data->when = MAP_LDAP_LATER; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_init( &data->mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + } else if ( strcasecmp( p, "everytime" ) == 0 ) { + data->when = MAP_LDAP_EVERYTIME; + } else { + /* ignore ... */ + } + } + } + + return ( void * )data; +} + +int +map_ldap_apply( + struct rewrite_builtin_map *map, + const char *filter, + struct berval *val + +) +{ + LDAP *ld; + LDAPMessage *res = NULL, *entry; + char **values; + int rc; + struct ldap_map_data *data = ( struct ldap_map_data * )map->lb_private; + LDAPURLDesc *lud = data->lud; + + int first_try = 1; + + assert( map != NULL ); + assert( map->lb_type == REWRITE_BUILTIN_MAP_LDAP ); + assert( map->lb_private != NULL ); + assert( filter != NULL ); + assert( val != NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + if ( data->when == MAP_LDAP_EVERYTIME ) { + rc = ldap_initialize( &ld, data->url ); + } else { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &data->mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = LDAP_SUCCESS; + + if ( data->when == MAP_LDAP_LATER && data->ld == NULL ) { + rc = ldap_initialize( &data->ld, data->url ); + } + + ld = data->ld; + } + + if ( rc != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + +do_bind: + if ( data->binddn != NULL ) { + rc = ldap_simple_bind_s( ld, data->binddn, data->bindpw ); + if ( rc == LDAP_SERVER_DOWN && first_try ) { + first_try = 0; + if ( ldap_initialize( &ld, data->url ) != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + goto do_bind; + } else if ( rc != REWRITE_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + } + + rc = ldap_search_s( ld, lud->lud_dn, lud->lud_scope, ( char * )filter, + lud->lud_attrs, data->attrsonly, &res ); + if ( rc == LDAP_SERVER_DOWN && first_try ) { + first_try = 0; + if ( ldap_initialize( &ld, data->url ) != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + goto do_bind; + } else if ( rc != REWRITE_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + + if ( ldap_count_entries( ld, res ) != 1 ) { + ldap_msgfree( res ); + rc = REWRITE_ERR; + goto rc_return; + } + + entry = ldap_first_entry( ld, res ); + assert( entry != NULL ); + + if ( data->attrsonly == 1 ) { + /* + * dn is newly allocated, so there's no need to strdup it + */ + val->bv_val = ldap_get_dn( ld, entry ); + } else { + values = ldap_get_values( ld, entry, lud->lud_attrs[ 0 ] ); + if ( values == NULL || values[ 0 ] == NULL ) { + if ( values != NULL ) { + ldap_value_free( values ); + } + ldap_msgfree( res ); + rc = REWRITE_ERR; + goto rc_return; + } + val->bv_val = strdup( values[ 0 ] ); + ldap_value_free( values ); + } + + ldap_msgfree( res ); + + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + goto rc_return; + } + val->bv_len = strlen( val->bv_val ); + +rc_return: + if ( data->when == MAP_LDAP_EVERYTIME ) { + if ( ld != NULL ) { + ldap_unbind_s( ld ); + } + } else { + data->ld = ld; +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &data->mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + } + + return rc; +} + diff --git a/libraries/librewrite/map.c b/libraries/librewrite/map.c new file mode 100644 index 0000000000..f22bb92587 --- /dev/null +++ b/libraries/librewrite/map.c @@ -0,0 +1,807 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include + +#include "rewrite-int.h" +#include "rewrite-map.h" + +/* + * Global data + */ +#ifdef USE_REWRITE_LDAP_PVT_THREADS +ldap_pvt_thread_mutex_t xpasswd_mutex; +static int xpasswd_mutex_init = 0; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + +/* + * Map parsing + * NOTE: these are old-fashion maps; new maps will be parsed on separate + * config lines, and referred by name. + */ +struct rewrite_map * +rewrite_xmap_parse( + struct rewrite_info *info, + const char *s, + const char **currpos +) +{ + struct rewrite_map *map; + + assert( info != NULL ); + assert( s != NULL ); + assert( currpos != NULL ); + + Debug( LDAP_DEBUG_ARGS, "rewrite_xmap_parse: %s\n%s%s", + s, "", "" ); + + *currpos = NULL; + + map = calloc( sizeof( struct rewrite_map ), 1 ); + if ( map == NULL ) { + Debug( LDAP_DEBUG_ANY, "rewrite_xmap_parse:" + " calloc failed\n%s%s%s", "", "", "" ); + return NULL; + } + + /* + * Experimental passwd map: + * replaces the uid with the matching gecos from /etc/passwd file + */ + if ( strncasecmp(s, "xpasswd", 7 ) == 0 ) { + map->lm_type = REWRITE_MAP_XPWDMAP; + map->lm_name = strdup( "xpasswd" ); + + assert( s[7] == '}' ); + *currpos = s + 8; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( !xpasswd_mutex_init ) { + xpasswd_mutex_init = 1; + if ( ldap_pvt_thread_mutex_init( &xpasswd_mutex ) ) { + free( map ); + return NULL; + } + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* Don't really care if fails */ + return map; + + /* + * Experimental file map: + * looks up key in a `key value' ascii file + */ + } else if ( strncasecmp(s, "xfile", 5 ) == 0 ) { + char *filename; + const char *p; + int l; + int c = 5; + + map->lm_type = REWRITE_MAP_XFILEMAP; + + if ( s[ c ] != '(' ) { + free( map ); + return NULL; + } + + /* Must start with '/' for security concerns */ + c++; + if ( s[ c ] != '/' ) { + free( map ); + return NULL; + } + + for ( p = s + c; p[ 0 ] != '\0' && p[ 0 ] != ')'; p++ ); + if ( p[ 0 ] != ')' ) { + free( map ); + return NULL; + } + + l = p - s - c; + filename = calloc( sizeof( char ), l + 1 ); + strncpy( filename, s + c, l ); + filename[ l ] = '\0'; + + map->lm_args = ( void * )fopen( filename, "r" ); + free( filename ); + + if ( map->lm_args == NULL ) { + free( map ); + return NULL; + } + + *currpos = p + 1; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) { + fclose( ( FILE * )map->lm_args ); + free( map ); + return NULL; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return map; + + /* + * Experimental ldap map: + * looks up key on the fly (not implemented!) + */ + } else if ( strncasecmp(s, "xldap", 5 ) == 0 ) { + char *p; + char *url; + int l, rc; + int c = 5; + LDAPURLDesc *lud; + + if ( s[ c ] != '(' ) { + free( map ); + return NULL; + } + c++; + + p = strchr( s, '}' ); + if ( p == NULL ) { + free( map ); + return NULL; + } + p--; + + *currpos = p + 2; + + /* + * Add two bytes for urlencoding of '%s' + */ + l = p - s - c; + url = calloc( sizeof( char ), l + 3 ); + strncpy( url, s + c, l ); + url[ l ] = '\0'; + + /* + * Urlencodes the '%s' for ldap_url_parse + */ + p = strchr( url, '%' ); + if ( p != NULL ) { + memmove( p + 3, p + 1, strlen( p + 1 ) + 1 ); + p[ 1 ] = '2'; + p[ 2 ] = '5'; + } + + rc = ldap_url_parse( url, &lud ); + free( url ); + + if ( rc != LDAP_SUCCESS ) { + free( map ); + return NULL; + } + assert( lud != NULL ); + + map->lm_args = ( void * )lud; + map->lm_type = REWRITE_MAP_XLDAPMAP; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) { + ldap_free_urldesc( lud ); + free( map ); + return NULL; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return map; + + /* Unhandled map */ + } + + return NULL; +} + +struct rewrite_map * +rewrite_map_parse( + struct rewrite_info *info, + const char *string, + const char **currpos +) +{ + struct rewrite_map *map = NULL; + struct rewrite_subst *subst = NULL; + char *s, *begin = NULL, *end; + const char *p; + int l, cnt; + + assert( info != NULL ); + assert( string != NULL ); + assert( currpos != NULL ); + + *currpos = NULL; + + /* + * Go to the end of the map invocation (the right closing brace) + */ + for ( p = string, cnt = 1; p[ 0 ] != '\0' && cnt > 0; p++ ) { + if ( p[ 0 ] == REWRITE_SUBMATCH_ESCAPE ) { + /* + * '\' marks the beginning of a new map + */ + if ( p[ 1 ] == '{' ) { + cnt++; + /* + * '\' followed by a digit may mark the beginning + * of an old map + */ + } else if ( isdigit( p[ 1 ] ) && p[ 2 ] == '{' ) { + cnt++; + p++; + } + p++; + } else if ( p[ 0 ] == '}' ) { + cnt--; + } + } + if ( cnt != 0 ) { + return NULL; + } + *currpos = p; + + /* + * Copy the map invocation + */ + l = p - string - 1; + s = calloc( sizeof( char ), l + 1 ); + strncpy( s, string, l ); + s[ l ] = 0; + + /* + * Isolate the map name (except for variable deref) + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + default: + begin = strchr( s, '(' ); + if ( begin == NULL ) { + free( s ); + return NULL; + } + begin[ 0 ] = '\0'; + begin++; + break; + } + + /* + * Check for special map types + */ + p = s; + switch ( p[ 0 ] ) { + case REWRITE_OPERATOR_SUBCONTEXT: + case REWRITE_OPERATOR_COMMAND: + case REWRITE_OPERATOR_VARIABLE_SET: + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + p++; + break; + } + + /* + * Variable set and get may be repeated to indicate session-wide + * instead of operation-wide variables + */ + switch ( p[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_SET: + case REWRITE_OPERATOR_VARIABLE_GET: + p++; + break; + } + + /* + * Variable get token can be appended to variable set to mean store + * AND rewrite + */ + if ( p[ 0 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + p++; + } + + /* + * Check the syntax of the variable name + */ + if ( !isalpha( p[ 0 ] ) ) { + free( s ); + return NULL; + } + for ( p++; p[ 0 ] != '\0'; p++ ) { + if ( !isalnum( p[ 0 ] ) ) { + free( s ); + return NULL; + } + } + + /* + * Isolate the argument of the map (except for variable deref) + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + default: + end = strrchr( begin, ')' ); + if ( end == NULL ) { + free( s ); + return NULL; + } + end[ 0 ] = '\0'; + + /* + * Compile the substitution pattern of the map argument + */ + subst = rewrite_subst_compile( info, begin ); + if ( subst == NULL ) { + free( s ); + return NULL; + } + break; + } + + /* + * Create the map + */ + map = calloc( sizeof( struct rewrite_map ), 1 ); + if ( map == NULL ) { + if ( subst != NULL ) { + free( subst ); + } + free( s ); + return NULL; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) { + if ( subst != NULL ) { + free( subst ); + } + free( s ); + free( map ); + return NULL; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* + * No subst for variable deref + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + default: + map->lm_subst = subst; + break; + } + + /* + * Parses special map types + */ + switch ( s[ 0 ] ) { + + /* + * Subcontext + */ + case REWRITE_OPERATOR_SUBCONTEXT: /* '>' */ + + /* + * Fetch the rewrite context + * it MUST have been defined previously + */ + map->lm_type = REWRITE_MAP_SUBCONTEXT; + map->lm_name = strdup( s + 1 ); + map->lm_data = rewrite_context_find( info, s + 1 ); + if ( map->lm_data == NULL ) { + free( s ); + free( map ); + return NULL; + } + break; + + /* + * External command + */ + case REWRITE_OPERATOR_COMMAND: /* '|' */ + free( map ); + map = NULL; + break; + + /* + * Variable set + */ + case REWRITE_OPERATOR_VARIABLE_SET: /* '&' */ + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_SET ) { + if ( s[ 2 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_SETW_SESN_VAR; + map->lm_name = strdup( s + 3 ); + } else { + map->lm_type = REWRITE_MAP_SET_SESN_VAR; + map->lm_name = strdup( s + 2 ); + } + } else { + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_SETW_OP_VAR; + map->lm_name = strdup( s + 2 ); + } else { + map->lm_type = REWRITE_MAP_SET_OP_VAR; + map->lm_name = strdup( s + 1 ); + } + } + break; + + /* + * Variable dereference + */ + case REWRITE_OPERATOR_VARIABLE_GET: /* '*' */ + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_GET_SESN_VAR; + map->lm_name = strdup( s + 2 ); + } else { + map->lm_type = REWRITE_MAP_GET_OP_VAR; + map->lm_name = strdup( s + 1 ); + } + break; + + /* + * Parameter + */ + case REWRITE_OPERATOR_PARAM_GET: /* '$' */ + map->lm_type = REWRITE_MAP_GET_PARAM; + map->lm_name = strdup( s + 1 ); + break; + + /* + * Built-in map + */ + default: + map->lm_type = REWRITE_MAP_BUILTIN; + map->lm_name = strdup( s ); + map->lm_data = rewrite_builtin_map_find( info, s ); + if ( map->lm_data == NULL ) { + return NULL; + } + break; + + } + + free( s ); + return map; +} + +/* + * Map key -> value resolution + * NOTE: these are old-fashion maps; new maps will be parsed on separate + * config lines, and referred by name. + */ +int +rewrite_xmap_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +) +{ + int rc = REWRITE_SUCCESS; + + assert( info != NULL ); + assert( op != NULL ); + assert( map != NULL ); + assert( key != NULL ); + assert( val != NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + switch ( map->lm_type ) { + case REWRITE_MAP_XPWDMAP: { + struct passwd *pwd; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &xpasswd_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + pwd = getpwnam( key->bv_val ); + if ( pwd == NULL ) { + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &xpasswd_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = REWRITE_NO_SUCH_OBJECT; + break; + } + + if ( pwd->pw_gecos != NULL && pwd->pw_gecos[0] != '\0' ) { + int l = strlen( pwd->pw_gecos ); + + val->bv_val = strdup( pwd->pw_gecos ); + if ( val->bv_val == NULL ) { + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &xpasswd_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = REWRITE_ERR; + break; + } + val->bv_len = l; + } else { + val->bv_val = strdup( key->bv_val ); + val->bv_len = key->bv_len; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &xpasswd_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + break; + } + + case REWRITE_MAP_XFILEMAP: { + char buf[1024]; + + if ( map->lm_args == NULL ) { + rc = REWRITE_ERR; + break; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rewind( ( FILE * )map->lm_args ); + + while ( fgets( buf, sizeof( buf ), ( FILE * )map->lm_args ) ) { + char *p; + int blen; + + blen = strlen( buf ); + if ( buf[ blen - 1 ] == '\n' ) { + buf[ blen - 1 ] = '\0'; + } + + p = strtok( buf, " " ); + if ( p == NULL ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + rc = REWRITE_ERR; + goto rc_return; + } + if ( strcasecmp( p, key->bv_val ) == 0 + && ( p = strtok( NULL, "" ) ) ) { + val->bv_val = strdup( p ); + if ( val->bv_val == NULL ) { + return REWRITE_ERR; + } + + val->bv_len = strlen( p ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + goto rc_return; + } + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = REWRITE_ERR; + + break; + } + + case REWRITE_MAP_XLDAPMAP: { + LDAP *ld; + char filter[ LDAP_FILT_MAXSIZ ]; + LDAPMessage *res = NULL, *entry; + LDAPURLDesc *lud = ( LDAPURLDesc * )map->lm_args; + int attrsonly = 0; + char **values; + + assert( lud != NULL ); + + /* + * No mutex because there is no write on the map data + */ + + ld = ldap_init( lud->lud_host, lud->lud_port ); + if ( ld == NULL ) { + rc = REWRITE_ERR; + goto rc_return; + } + + snprintf( filter, sizeof( filter ), lud->lud_filter, + key->bv_val ); + + if ( strcasecmp( lud->lud_attrs[ 0 ], "dn" ) == 0 ) { + attrsonly = 1; + } + rc = ldap_search_s( ld, lud->lud_dn, lud->lud_scope, + filter, lud->lud_attrs, attrsonly, &res ); + if ( rc != LDAP_SUCCESS ) { + ldap_unbind( ld ); + rc = REWRITE_ERR; + goto rc_return; + } + + if ( ldap_count_entries( ld, res ) != 1 ) { + ldap_unbind( ld ); + rc = REWRITE_ERR; + goto rc_return; + } + + entry = ldap_first_entry( ld, res ); + if ( entry == NULL ) { + ldap_msgfree( res ); + ldap_unbind( ld ); + rc = REWRITE_ERR; + goto rc_return; + } + if ( attrsonly == 1 ) { + val->bv_val = ldap_get_dn( ld, entry ); + if ( val->bv_val == NULL ) { + ldap_msgfree( res ); + ldap_unbind( ld ); + rc = REWRITE_ERR; + goto rc_return; + } + } else { + values = ldap_get_values( ld, entry, + lud->lud_attrs[0] ); + if ( values == NULL ) { + ldap_msgfree( res ); + ldap_unbind( ld ); + rc = REWRITE_ERR; + goto rc_return; + } + val->bv_val = strdup( values[ 0 ] ); + ldap_value_free( values ); + } + val->bv_len = strlen( val->bv_val ); + + ldap_msgfree( res ); + ldap_unbind( ld ); + + rc = REWRITE_SUCCESS; + } + } + +rc_return: + return rc; +} + +/* + * Applies the new map type + */ +int +rewrite_map_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +) +{ + int rc = REWRITE_SUCCESS; + + assert( info != NULL ); + assert( op != NULL ); + assert( map != NULL ); + assert( key != NULL ); + assert( val != NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + switch ( map->lm_type ) { + case REWRITE_MAP_SUBCONTEXT: + rc = rewrite_context_apply( info, op, + ( struct rewrite_context * )map->lm_data, + key->bv_val, &val->bv_val ); + if ( val->bv_val != NULL ) { + val->bv_len = strlen( val->bv_val ); + } + break; + + case REWRITE_MAP_SET_OP_VAR: + case REWRITE_MAP_SETW_OP_VAR: + rc = rewrite_var_set( &op->lo_vars, map->lm_name, + key->bv_val, 1 ) + ? REWRITE_SUCCESS : REWRITE_ERR; + if ( map->lm_type == REWRITE_MAP_SET_OP_VAR ) { + val->bv_val = strdup( "" ); + } else { + val->bv_val = strdup( key->bv_val ); + val->bv_len = key->bv_len; + } + break; + + case REWRITE_MAP_GET_OP_VAR: { + struct rewrite_var *var; + + var = rewrite_var_find( op->lo_vars, map->lm_name ); + if ( var == NULL ) { + rc = REWRITE_ERR; + } else { + val->bv_val = strdup( var->lv_value.bv_val ); + val->bv_len = var->lv_value.bv_len; + } + break; + } + + case REWRITE_MAP_SET_SESN_VAR: + case REWRITE_MAP_SETW_SESN_VAR: + if ( op->lo_cookie == NULL ) { + rc = REWRITE_ERR; + break; + } + rc = rewrite_session_var_set( info, op->lo_cookie, + map->lm_name, key->bv_val ); + if ( map->lm_type == REWRITE_MAP_SET_SESN_VAR ) { + val->bv_val = strdup( "" ); + } else { + val->bv_val = strdup( key->bv_val ); + val->bv_len = key->bv_len; + } + break; + + case REWRITE_MAP_GET_SESN_VAR: + rc = rewrite_session_var_get( info, op->lo_cookie, + map->lm_name, val ); + break; + + case REWRITE_MAP_GET_PARAM: + rc = rewrite_param_get( info, map->lm_name, val ); + break; + + case REWRITE_MAP_BUILTIN: { + struct rewrite_builtin_map *bmap = map->lm_data; + switch ( bmap->lb_type ) { + case REWRITE_BUILTIN_MAP_LDAP: + rc = map_ldap_apply( bmap, key->bv_val, val ); + break; + default: + rc = REWRITE_ERR; + break; + } + break; + } + + default: + rc = REWRITE_ERR; + break; + } + + return rc; +} + diff --git a/libraries/librewrite/params.c b/libraries/librewrite/params.c new file mode 100644 index 0000000000..07daa0aabd --- /dev/null +++ b/libraries/librewrite/params.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" + +/* + * Defines and inits a variable with global scope + */ +int +rewrite_param_set( + struct rewrite_info *info, + const char *name, + const char *value +) +{ + struct rewrite_var *var; + + assert( info != NULL ); + assert( name != NULL ); + assert( value != NULL ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + var = rewrite_var_find( info->li_params, name ); + if ( var != NULL ) { + assert( var->lv_value.bv_val != NULL ); + free( var->lv_value.bv_val ); + var->lv_value.bv_val = strdup( value ); + var->lv_value.bv_len = strlen( value ); + } else { + var = rewrite_var_insert( &info->li_params, name, value ); + if ( var == NULL ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + return REWRITE_ERR; + } + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + +/* + * Gets a var with global scope + */ +int +rewrite_param_get( + struct rewrite_info *info, + const char *name, + struct berval *value +) +{ + struct rewrite_var *var; + + assert( info != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + value->bv_val = NULL; + value->bv_len = 0; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_rlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + var = rewrite_var_find( info->li_params, name ); + if ( var == NULL ) { + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_runlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_ERR; + } else { + value->bv_val = strdup( var->lv_value.bv_val ); + value->bv_len = var->lv_value.bv_len; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_runlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + +/* + * Destroys the parameter tree + */ +int +rewrite_param_destroy( + struct rewrite_info *info +) +{ + int count; + + assert( info != NULL ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + count = avl_free( info->li_params, NULL ); + info->li_params = NULL; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + diff --git a/libraries/librewrite/parse.c b/libraries/librewrite/parse.c new file mode 100644 index 0000000000..bf4426cebf --- /dev/null +++ b/libraries/librewrite/parse.c @@ -0,0 +1,127 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" + +static int +parse_line( + char **argv, + int *argc, + int maxargs, + char *buf +) +{ + char *p, *begin; + int in_quoted_field = 0, cnt = 0; + char quote = '\0'; + + for ( p = buf; isspace( p[ 0 ] ); p++ ); + + if ( p[ 0 ] == '#' ) { + return 0; + } + + for ( begin = p; p[ 0 ] != '\0'; p++ ) { + if ( p[ 0 ] == '\\' ) { + p++; + } else if ( p[ 0 ] == '\'' || p[ 0 ] == '\"') { + if ( in_quoted_field && p[ 0 ] == quote ) { + in_quoted_field = 1 - in_quoted_field; + quote = '\0'; + p[ 0 ] = '\0'; + argv[ cnt ] = begin; + if ( ++cnt == maxargs ) { + *argc = cnt; + return 1; + } + for ( p++; isspace( p[ 0 ] ); p++ ); + begin = p; + p--; + + } else if ( !in_quoted_field ) { + if ( p != begin ) { + return -1; + } + begin++; + in_quoted_field = 1 - in_quoted_field; + quote = p[ 0 ]; + } + } else if ( isspace( p[ 0 ] ) && !in_quoted_field ) { + p[ 0 ] = '\0'; + argv[ cnt ] = begin; + + if ( ++cnt == maxargs ) { + *argc = cnt; + return 1; + } + + for ( p++; isspace( p[ 0 ] ); p++ ); + begin = p; + p--; + } + } + + *argc = cnt; + + return 1; +} + +int +read_rewrite( + FILE *fin, + struct rewrite_info *info +) +{ + char buf[ 1024 ]; + char *argv[11]; + int argc, lineno; + + /* + * Empty rule at the beginning of the context + */ + + for ( lineno = 0; fgets( buf, sizeof( buf ), fin ); lineno++ ) { + switch ( parse_line( argv, &argc, sizeof( argv ) - 1, buf ) ) { + case -1: + return REWRITE_ERR; + case 0: + break; + case 1: + if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) { + int rc; + rc = rewrite_parse( info, "file", lineno, + argc, argv ); + if ( rc != REWRITE_SUCCESS ) { + return rc; + } + } + break; + } + } + + return REWRITE_SUCCESS; +} + diff --git a/libraries/librewrite/rewrite-int.h b/libraries/librewrite/rewrite-int.h new file mode 100644 index 0000000000..375e6d5baa --- /dev/null +++ b/libraries/librewrite/rewrite-int.h @@ -0,0 +1,554 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#ifndef REWRITE_INT_H +#define REWRITE_INT_H + +/* + * These are required by every file of the library, so they're included here + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../libldap/ldap-int.h" + +#include + +#include + +/* Uncomment to use ldap pvt threads */ +#define USE_REWRITE_LDAP_PVT_THREADS +#include + +/* + * For details, see RATIONALE. + */ + +#define REWRITE_MAX_MATCH 11 /* 0: overall string; 1-9: submatches */ +#define REWRITE_MAX_PASSES 100 + +/* + * Submatch escape char + */ +//#define REWRITE_SUBMATCH_ESCAPE '\\' +#define REWRITE_SUBMATCH_ESCAPE '%' + +/* + * REGEX flags + */ + +#define REWRITE_FLAG_HONORCASE 'C' +#define REWRITE_FLAG_BASICREGEX 'R' + +/* + * Action flags + */ +#define REWRITE_FLAG_EXECONCE ':' +#define REWRITE_FLAG_STOP '@' +#define REWRITE_FLAG_UNWILLING '#' +#define REWRITE_FLAG_GOTO 'G' /* requires an arg */ +#define REWRITE_FLAG_IGNORE_ERR 'I' + +/* + * Map operators + */ +#define REWRITE_OPERATOR_SUBCONTEXT '>' +#define REWRITE_OPERATOR_COMMAND '|' +#define REWRITE_OPERATOR_VARIABLE_SET '&' +#define REWRITE_OPERATOR_VARIABLE_GET '*' +#define REWRITE_OPERATOR_PARAM_GET '$' + + +/*********** + * PRIVATE * + ***********/ + +/* + * Action + */ +struct rewrite_action { + struct rewrite_action *la_next; + +#define REWRITE_ACTION_STOP 0x0001 +#define REWRITE_ACTION_UNWILLING 0x0002 +#define REWRITE_ACTION_GOTO 0x0003 +#define REWRITE_ACTION_IGNORE_ERR 0x0004 + int la_type; + void *la_args; +}; + +/* + * Map + */ +struct rewrite_map { + + /* + * Legacy stuff + */ +#define REWRITE_MAP_XFILEMAP 0x0001 /* Rough implementation! */ +#define REWRITE_MAP_XPWDMAP 0x0002 /* uid -> gecos */ +#define REWRITE_MAP_XLDAPMAP 0x0003 /* Not implemented yet! */ + + /* + * Maps with args + */ +#define REWRITE_MAP_SUBCONTEXT 0x0101 + +#define REWRITE_MAP_SET_OP_VAR 0x0102 +#define REWRITE_MAP_SETW_OP_VAR 0x0103 +#define REWRITE_MAP_GET_OP_VAR 0x0104 +#define REWRITE_MAP_SET_SESN_VAR 0x0105 +#define REWRITE_MAP_SETW_SESN_VAR 0x0106 +#define REWRITE_MAP_GET_SESN_VAR 0x0107 +#define REWRITE_MAP_GET_PARAM 0x0108 +#define REWRITE_MAP_BUILTIN 0x0109 + int lm_type; + + char *lm_name; + void *lm_data; + + /* + * Old maps store private data in _lm_args; + * new maps store the substitution pattern in _lm_subst + */ + union { + void *_lm_args; + struct rewrite_subst *_lm_subst; + } lm_union; +#define lm_args lm_union._lm_args +#define lm_subst lm_union._lm_subst + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_t lm_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ +}; + +/* + * Builtin maps + */ +struct rewrite_builtin_map { +#define REWRITE_BUILTIN_MAP_LDAP 0x0201 + int lb_type; + char *lb_name; + void *lb_private; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_t lb_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ +}; + +/* + * Submatch substitution + */ +struct rewrite_submatch { +#define REWRITE_SUBMATCH_ASIS 0x0000 +#define REWRITE_SUBMATCH_XMAP 0x0001 +#define REWRITE_SUBMATCH_MAP_W_ARG 0x0002 + int ls_type; + struct rewrite_map *ls_map; + int ls_submatch; + /* + * The first one represents the index of the submatch in case + * the map has single submatch as argument; + * the latter represents the map argument scheme in case + * the map has substitution string argument form + */ +}; + +/* + * Pattern substitution + */ +struct rewrite_subst { + size_t lt_subs_len; + struct berval **lt_subs; + + int lt_num_submatch; + struct rewrite_submatch **lt_submatch; +}; + +/* + * Rule + */ +struct rewrite_rule { + struct rewrite_rule *lr_next; + struct rewrite_rule *lr_prev; + + char *lr_pattern; + char *lr_subststring; + char *lr_flagstring; + regex_t lr_regex; + + /* + * I was thinking about some kind of per-rule mutex, but there's + * probably no need, because rules after compilation are only read; + * however, I need to check whether regexec is reentrant ... + */ + + struct rewrite_subst *lr_subst; + +#define REWRITE_REGEX_ICASE REG_ICASE +#define REWRITE_REGEX_EXTENDED REG_EXTENDED + int lr_flags; + +#define REWRITE_RECURSE 0x0001 +#define REWRITE_EXEC_ONCE 0x0002 + int lr_mode; + + struct rewrite_action *lr_action; +}; + +/* + * Rewrite Context (set of rules) + */ +struct rewrite_context { + char *lc_name; + struct rewrite_context *lc_alias; + struct rewrite_rule *lc_rule; +}; + +/* + * Session + */ +struct rewrite_session { + void *ls_cookie; + Avlnode *ls_vars; +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_t ls_vars_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ +}; + +/* + * Variable + */ +struct rewrite_var { + char *lv_name; + struct berval lv_value; +}; + +/* + * Operation + */ +struct rewrite_op { + int lo_num_passes; + int lo_depth; + char *lo_string; + char *lo_result; + Avlnode *lo_vars; + const void *lo_cookie; +}; + + +/********** + * PUBLIC * + **********/ + +/* + * Rewrite info + */ +struct rewrite_info { + Avlnode *li_context; + Avlnode *li_maps; + /* + * No global mutex because maps are read only at + * config time + */ + Avlnode *li_params; + Avlnode *li_cookies; + int li_num_cookies; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_t li_params_mutex; + ldap_pvt_thread_rdwr_t li_cookies_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* + * Default to `off'; + * use `rewriteEngine {on|off}' directive to alter + */ + int li_state; + + /* + * Defaults to REWRITE_MAXPASSES; + * use `rewriteMaxPasses numPasses' directive to alter + */ +#define REWRITE_MAXPASSES 100 + int li_max_passes; + + /* + * Behavior in case a NULL or non-existent context is required + */ + int li_rewrite_mode; +}; + +/*********** + * PRIVATE * + ***********/ + + +/* + * Maps + */ + +/* + * Parses a map (also in legacy 'x' version) + */ +extern struct rewrite_map * +rewrite_map_parse( + struct rewrite_info *info, + const char *s, + const char **end +); + +extern struct rewrite_map * +rewrite_xmap_parse( + struct rewrite_info *info, + const char *s, + const char **end +); + +/* + * Resolves key in val by means of map (also in legacy 'x' version) + */ +extern int +rewrite_map_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +); + +extern int +rewrite_xmap_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +); + + +/* + * Submatch substitution + */ + +/* + * Compiles a substitution pattern + */ +extern struct rewrite_subst * +rewrite_subst_compile( + struct rewrite_info *info, + const char *result +); + +/* + * Substitutes a portion of rewritten string according to substitution + * pattern using submatches + */ +extern int +rewrite_subst_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_subst *subst, + const char *string, + const regmatch_t *match, + struct berval *val +); + + +/* + * Rules + */ + +/* + * Compiles the rule and appends it at the running context + */ +extern int +rewrite_rule_compile( + struct rewrite_info *info, + struct rewrite_context *context, + const char *pattern, + const char *result, + const char *flagstring +); + +/* + * Rewrites string according to rule; may return: + * REWRITE_REGEXEC_OK: fine; if *result != NULL rule matched + * and rewrite succeeded. + * REWRITE_REGEXEC_STOP: fine, rule matched; stop processing + * following rules + * REWRITE_REGEXEC_UNWILL: rule matched; force 'unwilling to perform' + * REWRITE_REGEXEC_ERR: an error occurred + */ +extern int +rewrite_rule_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_rule *rule, + const char *string, + char **result +); + +/* + * Sessions + */ + +/* + * Fetches a struct rewrite_session + */ +extern struct rewrite_session * +rewrite_session_find( + struct rewrite_info *info, + const void *cookie +); + +/* + * Defines and inits a variable with session scope + */ +extern int +rewrite_session_var_set( + struct rewrite_info *info, + const void *cookie, + const char *name, + const char *value +); + +/* + * Gets a var with session scope + */ +extern int +rewrite_session_var_get( + struct rewrite_info *info, + const void *cookie, + const char *name, + struct berval *val +); + +/* + * Deletes a session + */ +extern int +rewrite_session_delete( + struct rewrite_info *info, + const void *cookie +); + +/* + * Destroys the cookie tree + */ +extern int +rewrite_session_destroy( + struct rewrite_info *info +); + + +/* + * Vars + */ + +/* + * Finds a var + */ +extern struct rewrite_var * +rewrite_var_find( + Avlnode *tree, + const char *name +); + +/* + * Inserts a newly created var + */ +extern struct rewrite_var * +rewrite_var_insert( + Avlnode **tree, + const char *name, + const char *value +); + +/* + * Sets/inserts a var + */ +extern struct rewrite_var * +rewrite_var_set( + Avlnode **tree, + const char *name, + const char *value, + int insert +); + +/* + * Deletes a var tree + */ +extern int +rewrite_var_delete( + Avlnode *tree +); + + +/* + * Contexts + */ + +/* + * Finds the context named rewriteContext in the context tree + */ +extern struct rewrite_context * +rewrite_context_find( + struct rewrite_info *info, + const char *rewriteContext +); + +/* + * Creates a new context called rewriteContext and stores in into the tree + */ +extern struct rewrite_context * +rewrite_context_create( + struct rewrite_info *info, + const char *rewriteContext +); + +/* + * Rewrites string according to context; may return: + * OK: fine; if *result != NULL rule matched and rewrite succeeded. + * STOP: fine, rule matched; stop processing following rules + * UNWILL: rule matched; force 'unwilling to perform' + */ +extern int +rewrite_context_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_context *context, + const char *string, + char **result +); + +#endif /* REWRITE_INT_H */ + diff --git a/libraries/librewrite/rewrite-map.h b/libraries/librewrite/rewrite-map.h new file mode 100644 index 0000000000..c01a4c769c --- /dev/null +++ b/libraries/librewrite/rewrite-map.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#ifndef MAP_H +#define MAP_H + +/* + * Retrieves a builtin map + */ +struct rewrite_builtin_map * +rewrite_builtin_map_find( + struct rewrite_info *info, + const char *name +); + + +/* + * LDAP map + */ +void * +map_ldap_parse( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +); + +int +map_ldap_apply( struct rewrite_builtin_map *map, + const char *filter, + struct berval *val +); + +#endif /* MAP_H */ diff --git a/libraries/librewrite/rewrite.c b/libraries/librewrite/rewrite.c new file mode 100644 index 0000000000..6753f99600 --- /dev/null +++ b/libraries/librewrite/rewrite.c @@ -0,0 +1,147 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +int ldap_debug; +int ldap_syslog; +int ldap_syslog_level; + +extern int +read_rewrite( + FILE *fin, + struct rewrite_info *info +); + +char * +apply( + FILE *fin, + const char *rewriteContext, + const char *arg +) +{ + struct rewrite_info *info; + char *string, *sep, *result = NULL; + int rc; + void *cookie = &info; + + info = rewrite_info_init(REWRITE_MODE_ERR); + + if ( read_rewrite( fin, info ) != 0 ) { + exit( EXIT_FAILURE ); + } + + rewrite_param_set( info, "prog", "rewrite" ); + + rewrite_session_init( info, cookie ); + + string = strdup( arg ); + for ( sep = strchr( rewriteContext, ',' ); + rewriteContext != NULL; + rewriteContext = sep, + sep ? sep = strchr( rewriteContext, ',' ) : NULL ) { + if ( sep != NULL ) { + sep[ 0 ] = '\0'; + sep++; + } + // rc = rewrite( info, rewriteContext, string, &result ); + rc = rewrite_session( info, rewriteContext, string, + cookie, &result ); + + fprintf( stdout, "%s -> %s\n", string, + ( result ? result : "unwilling to perform" ) ); + if ( result == NULL ) { + break; + } + free( string ); + string = result; + } + + rewrite_session_delete( info, cookie ); + + return result; +} + +int +main( int argc, char *argv[] ) +{ + FILE *fin = NULL; + char *rewriteContext = REWRITE_DEFAULT_CONTEXT; + + while ( 1 ) { + int opt = getopt( argc, argv, "f:hr:" ); + + if ( opt == EOF ) { + break; + } + + switch ( opt ) { + case 'f': + fin = fopen( optarg, "r" ); + if ( fin == NULL ) { + fprintf( stderr, "unable to open file '%s'\n", + optarg ); + exit( EXIT_FAILURE ); + } + break; + + case 'h': + fprintf( stderr, + "usage: rewrite [options] string\n" + "\n" + "\t\t-f file\t\tconfiguration file\n" + "\t\t-r rule[s]\tlist of comma-separated rules\n" + "\n" + "\tsyntax:\n" + "\t\trewriteEngine\t{on|off}\n" + "\t\trewriteContext\tcontextName [alias aliasedContextName]\n" + "\t\trewriteRule\tpattern subst [flags]\n" + "\n" + ); + exit( EXIT_SUCCESS ); + + case 'r': + rewriteContext = strdup( optarg ); + break; + } + } + + if ( optind >= argc ) { + return -1; + } + + apply( ( fin ? fin : stdin ), rewriteContext, argv[ optind ] ); + + return 0; +} + diff --git a/libraries/librewrite/rule.c b/libraries/librewrite/rule.c new file mode 100644 index 0000000000..2347383480 --- /dev/null +++ b/libraries/librewrite/rule.c @@ -0,0 +1,393 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" + +/* + * Appends a rule to the double linked list of rules + * Helper for rewrite_rule_compile + */ +static int +append_rule( + struct rewrite_context *context, + struct rewrite_rule *rule +) +{ + struct rewrite_rule *r; + + assert( context != NULL ); + assert( context->lc_rule != NULL ); + assert( rule != NULL ); + + for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next ); + r->lr_next = rule; + rule->lr_prev = r; + + return REWRITE_SUCCESS; +} + +/* + * Appends an action to the linked list of actions + * Helper for rewrite_rule_compile + */ +static int +append_action( + struct rewrite_action *base, + struct rewrite_action *action +) +{ + struct rewrite_action *a; + + assert( base != NULL ); + assert( action != NULL ); + + for ( a = base; a->la_next != NULL; a = a->la_next ); + a->la_next = action; + + return REWRITE_SUCCESS; +} + +/* + * In case of error it returns NULL and does not free all the memory + * it allocated; as this is a once only phase, and an error at this stage + * would require the server to stop, there is no need to be paranoid + * about memory allocation + */ +int +rewrite_rule_compile( + struct rewrite_info *info, + struct rewrite_context *context, + const char *pattern, + const char *result, + const char *flagstring +) +{ + int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE; + int mode = REWRITE_RECURSE; + + struct rewrite_rule *rule = NULL; + struct rewrite_subst *subst = NULL; + struct rewrite_action *action = NULL, *first_action = NULL; + + const char *p; + + assert( info != NULL ); + assert( context != NULL ); + assert( pattern != NULL ); + assert( result != NULL ); + + /* + * A null flagstring should be allowed + */ + + /* + * Take care of substitution string + */ + subst = rewrite_subst_compile( info, result ); + if ( subst == NULL ) { + return REWRITE_ERR; + } + + /* + * Take care of flags + */ + for ( p = flagstring; p[ 0 ] != '\0'; p++ ) { + switch( p[ 0 ] ) { + + /* + * REGEX flags + */ + case REWRITE_FLAG_HONORCASE: /* 'C' */ + /* + * Honor case (default is case insensitive) + */ + flags &= ~REWRITE_REGEX_ICASE; + break; + + case REWRITE_FLAG_BASICREGEX: /* 'R' */ + /* + * Use POSIX Basic Regular Expression syntax + * instead of POSIX Extended Regular Expression + * syntax (default) + */ + flags &= ~REWRITE_REGEX_EXTENDED; + break; + + /* + * Execution mode flags + */ + case REWRITE_FLAG_EXECONCE: /* ':' */ + /* + * Apply rule once only + */ + mode &= ~REWRITE_RECURSE; + mode |= REWRITE_EXEC_ONCE; + break; + + /* + * Special action flags + */ + case REWRITE_FLAG_STOP: /* '@' */ + /* + * Bail out after applying rule + */ + action = calloc( sizeof( struct rewrite_action ), 1 ); + if ( action == NULL ) { + /* cleanup ... */ + return REWRITE_ERR; + } + + mode &= ~REWRITE_RECURSE; + mode |= REWRITE_EXEC_ONCE; + action->la_type = REWRITE_ACTION_STOP; + break; + + case REWRITE_FLAG_UNWILLING: /* '#' */ + /* + * Matching objs will be marked as gone! + */ + action = calloc( sizeof( struct rewrite_action ), 1 ); + if ( action == NULL ) { + /* cleanup ... */ + return REWRITE_ERR; + } + + mode &= ~REWRITE_RECURSE; + mode |= REWRITE_EXEC_ONCE; + action->la_type = REWRITE_ACTION_UNWILLING; + break; + + case REWRITE_FLAG_GOTO: { /* 'G' */ + /* + * After applying rule, jump N rules + */ + + char buf[16], *q; + size_t l; + int *d; + + if ( p[ 1 ] != '{' ) { + /* XXX Need to free stuff */ + return REWRITE_ERR; + } + + q = strchr( p + 2, '}' ); + if ( q == NULL ) { + /* XXX Need to free stuff */ + return REWRITE_ERR; + } + + l = q - p + 2; + if ( l >= sizeof( buf ) ) { + /* XXX Need to free stuff */ + return REWRITE_ERR; + } + strncpy( buf, p + 2, l ); + buf[ l ] = '\0'; + + d = malloc( sizeof( int ) ); + if ( d == NULL ) { + /* XXX Need to free stuff */ + return REWRITE_ERR; + } + d[ 0 ] = atoi( buf ); + + action = calloc( sizeof( struct rewrite_action ), 1 ); + if ( action == NULL ) { + /* cleanup ... */ + return REWRITE_ERR; + } + action->la_type = REWRITE_ACTION_GOTO; + action->la_args = (void *)d; + + p = q; /* p is incremented by the for ... */ + + break; + } + + case REWRITE_FLAG_IGNORE_ERR: /* 'I' */ + /* + * Ignore errors! + */ + action = calloc( sizeof( struct rewrite_action ), 1 ); + if ( action == NULL ) { + /* cleanup ... */ + return REWRITE_ERR; + } + + action->la_type = REWRITE_ACTION_IGNORE_ERR; + break; + + /* + * Other flags ... + */ + default: + /* + * Unimplemented feature (complain only) + */ + break; + } + + /* + * Stupid way to append to a list ... + */ + if ( action != NULL ) { + if ( first_action == NULL ) { + first_action = action; + } else { + append_action( first_action, action ); + } + action = NULL; + } + } + + /* + * Finally, rule allocation + */ + rule = calloc( sizeof( struct rewrite_rule ), 1 ); + if ( rule == NULL ) { + // charray_free( res ); + /* + * XXX need to free the value subst stuff! + */ + return REWRITE_ERR; + } + + /* + * REGEX compilation (luckily I don't need to take care of this ...) + */ + if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) { + // charray_free( res ); + /* + *XXX need to free the value subst stuff! + */ + free( rule ); + return REWRITE_ERR; + } + + /* + * Just to remember them ... + */ + rule->lr_pattern = strdup( pattern ); + rule->lr_subststring = strdup( result ); + rule->lr_flagstring = strdup( flagstring ); + + /* + * Load compiled data into rule + */ + rule->lr_subst = subst; + + /* + * Set various parameters + */ + rule->lr_flags = flags; /* don't really need any longer ... */ + rule->lr_mode = mode; + rule->lr_action = first_action; + + /* + * Append rule at the end of the rewrite context + */ + append_rule( context, rule ); + + return REWRITE_SUCCESS; +} + +/* + * Rewrites string according to rule; may return: + * OK: fine; if *result != NULL rule matched and rewrite succeeded. + * STOP: fine, rule matched; stop processing following rules + * UNWILL: rule matched; force 'unwilling to perform' + */ +int +rewrite_rule_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_rule *rule, + const char *arg, + char **result + ) +{ + size_t nmatch = REWRITE_MAX_MATCH; + regmatch_t match[ REWRITE_MAX_MATCH ]; + + int rc = REWRITE_SUCCESS; + + char *string; + struct berval val; + + assert( info != NULL ); + assert( op != NULL ); + assert( rule != NULL ); + assert( arg != NULL ); + assert( result != NULL ); + + *result = NULL; + + string = strdup( arg ); + if ( string == NULL ) { + return REWRITE_REGEXEC_ERR; + } + + /* + * In case recursive match is required (default) + */ +recurse:; + + Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply" + " rule='%s' string='%s'\n%s", + rule->lr_pattern, string, "" ); + + op->lo_num_passes++; + if ( regexec( &rule->lr_regex, string, nmatch, match, 0 ) != 0 ) { + if ( *result == NULL ) { + free( string ); + } + + /* + * No match is OK; *result = NULL means no match + */ + return REWRITE_REGEXEC_OK; + } + + rc = rewrite_subst_apply( info, op, rule->lr_subst, string, + match, &val ); + + *result = val.bv_val; + free( string ); + + if ( rc != REWRITE_REGEXEC_OK ) { + return rc; + } + + if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE + && op->lo_num_passes <= info->li_max_passes ) { + string = *result; + goto recurse; + } + + return REWRITE_REGEXEC_OK; +} + diff --git a/libraries/librewrite/session.c b/libraries/librewrite/session.c new file mode 100644 index 0000000000..b372eff5da --- /dev/null +++ b/libraries/librewrite/session.c @@ -0,0 +1,330 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" + +/* + * Compares two cookies + */ +static int +rewrite_cookie_cmp( + const void *c1, + const void *c2 +) +{ + struct rewrite_session *s1, *s2; + + s1 = ( struct rewrite_session * )c1; + s2 = ( struct rewrite_session * )c2; + + assert( s1 != NULL ); + assert( s2 != NULL ); + assert( s1->ls_cookie != NULL ); + assert( s2->ls_cookie != NULL ); + + return ( ( s1->ls_cookie < s2->ls_cookie ) ? -1 : + ( ( s1->ls_cookie > s2->ls_cookie ) ? 1 : 0 ) ); +} + +/* + * Duplicate cookies? + */ +static int +rewrite_cookie_dup( + void *c1, + void *c2 +) +{ + struct rewrite_session *s1, *s2; + + s1 = ( struct rewrite_session * )c1; + s2 = ( struct rewrite_session * )c2; + + assert( s1 != NULL ); + assert( s2 != NULL ); + assert( s1->ls_cookie != NULL ); + assert( s2->ls_cookie != NULL ); + + return ( ( s1->ls_cookie == s2->ls_cookie ) ? -1 : 0 ); +} + +/* + * Inits a session + */ +struct rewrite_session * +rewrite_session_init( + struct rewrite_info *info, + const void *cookie +) +{ + struct rewrite_session *session; + int rc; + + assert( info != NULL ); + assert( cookie != NULL ); + + session = calloc( sizeof( struct rewrite_session ), 1 ); + if ( session == NULL ) { + return NULL; + } + session->ls_cookie = ( void * )cookie; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_rdwr_init( &session->ls_vars_mutex ) ) { + free( session ); + return NULL; + } + ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = avl_insert( &info->li_cookies, ( caddr_t )session, + rewrite_cookie_cmp, rewrite_cookie_dup ); + info->li_num_cookies++; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( rc != 0 ) { + free( session ); + return NULL; + } + + return session; +} + +/* + * Fetches a session + */ +struct rewrite_session * +rewrite_session_find( + struct rewrite_info *info, + const void *cookie +) +{ + struct rewrite_session *session, tmp; + + assert( info != NULL ); + assert( cookie != NULL ); + + tmp.ls_cookie = ( void * )cookie; +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_rlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + session = ( struct rewrite_session * )avl_find( info->li_cookies, + ( caddr_t )&tmp, rewrite_cookie_cmp ); +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_runlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return session; + +} + +/* + * Defines and inits a var with session scope + */ +int +rewrite_session_var_set( + struct rewrite_info *info, + const void *cookie, + const char *name, + const char *value +) +{ + struct rewrite_session *session; + struct rewrite_var *var; + + assert( info != NULL ); + assert( cookie != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + session = rewrite_session_find( info, cookie ); + if ( session == NULL ) { + session = rewrite_session_init( info, cookie ); + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + var = rewrite_var_find( session->ls_vars, name ); + if ( var != NULL ) { + assert( var->lv_value.bv_val != NULL ); + free( var->lv_value.bv_val ); + var->lv_value.bv_val = strdup( value ); + var->lv_value.bv_len = strlen( value ); + } else { + var = rewrite_var_insert( &session->ls_vars, name, value ); + if ( var == NULL ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + return REWRITE_ERR; + } + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + +/* + * Gets a var with session scope + */ +int +rewrite_session_var_get( + struct rewrite_info *info, + const void *cookie, + const char *name, + struct berval *value +) +{ + struct rewrite_session *session; + struct rewrite_var *var; + + assert( info != NULL ); + assert( cookie != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + value->bv_val = NULL; + value->bv_len = 0; + + if ( cookie == NULL ) { + return REWRITE_ERR; + } + + session = rewrite_session_find( info, cookie ); + if ( session == NULL ) { + return REWRITE_ERR; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_rlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + var = rewrite_var_find( session->ls_vars, name ); + if ( var == NULL ) { + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_runlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_ERR; + } else { + value->bv_val = strdup( var->lv_value.bv_val ); + value->bv_len = var->lv_value.bv_len; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_runlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + +/* + * Deletes a session + */ +int +rewrite_session_delete( + struct rewrite_info *info, + const void *cookie +) +{ + struct rewrite_session *session, tmp; + + assert( info != NULL ); + assert( cookie != NULL ); + + tmp.ls_cookie = ( void * )cookie; + + session = rewrite_session_find( info, cookie ); + + if ( session != NULL ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + rewrite_var_delete( session->ls_vars ); +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + assert( info->li_num_cookies > 0 ); + info->li_num_cookies--; + + /* + * There is nothing to delete in the return value + */ + avl_delete( &info->li_cookies, ( caddr_t )&tmp, rewrite_cookie_cmp ); +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + +/* + * Destroys the cookie tree + */ +int +rewrite_session_destroy( + struct rewrite_info *info +) +{ + int count; + + assert( info != NULL ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* + * Should call per-session destruction routine ... + */ + + count = avl_free( info->li_cookies, NULL ); + info->li_cookies = NULL; + assert( count == info->li_num_cookies ); + info->li_num_cookies = 0; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + diff --git a/libraries/librewrite/subst.c b/libraries/librewrite/subst.c new file mode 100644 index 0000000000..2f0645f6d4 --- /dev/null +++ b/libraries/librewrite/subst.c @@ -0,0 +1,427 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" + +/* + * Compiles a substitution pattern + */ +struct rewrite_subst * +rewrite_subst_compile( + struct rewrite_info *info, + const char *result +) +{ + size_t subs_len; + struct berval **subs = NULL; + struct rewrite_submatch **submatch = NULL; + + struct rewrite_subst *s = NULL; + + const char *begin, *p; + int nsub = 0, l; + + assert( info != NULL ); + assert( result != NULL ); + + /* + * Take care of substitution string + */ + for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) { + + /* + * Keep only single escapes '\' + */ + if ( p[ 0 ] != REWRITE_SUBMATCH_ESCAPE ) { + continue; + } else if ( p[ 1 ] == REWRITE_SUBMATCH_ESCAPE ) { + continue; + } + + nsub++; + + subs = (struct berval **)realloc( subs, + sizeof( struct berval * )*( nsub + 1 ) ); + if ( subs == NULL ) { + /* cleanup */ + return NULL; + } + subs[ nsub ] = NULL; + + /* + * I think an `if l > 0' at runtime is better outside than + * inside a function call ... + */ + l = p - begin; + if ( l > 0 ) { + subs_len += l; + subs[ nsub - 1 ] = + calloc( sizeof( struct berval ), 1 ); + if ( subs[ nsub - 1 ] == NULL ) { + /* cleanup */ + return NULL; + } + subs[ nsub - 1 ]->bv_len = l; + subs[ nsub - 1 ]->bv_val = malloc( l + 1 ); + if ( subs[ nsub - 1 ]->bv_val == NULL ) { + return NULL; + } + strncpy( subs[ nsub - 1 ]->bv_val, begin, l ); + subs[ nsub - 1 ]->bv_val[ l ] = '\0'; + } else { + subs[ nsub - 1 ] = NULL; + } + + /* + * Substitution pattern + */ + if ( isdigit( p[ 1 ] ) ) { + int d = p[ 1 ] - '0'; + + /* + * Add a new value substitution scheme + */ + submatch = realloc( submatch, + sizeof( struct rewrite_submatch * )*( nsub + 1 ) ); + if ( submatch == NULL ) { + /* cleanup */ + return NULL; + } + submatch[ nsub ] = NULL; + submatch[ nsub - 1 ] = + calloc( sizeof( struct rewrite_submatch ), 1 ); + if ( submatch[ nsub - 1 ] == NULL ) { + /* cleanup */ + return NULL; + } + submatch[ nsub - 1 ]->ls_submatch = d; + + /* + * If there is no argument, use default + * (substitute substring as is) + */ + if ( p[ 2 ] != '{' ) { + submatch[ nsub - 1 ]->ls_type = + REWRITE_SUBMATCH_ASIS; + begin = ++p + 1; + } else { + struct rewrite_map *map; + + submatch[ nsub - 1 ]->ls_type = + REWRITE_SUBMATCH_XMAP; + + map = rewrite_xmap_parse( info, + p + 3, &begin ); + if ( map == NULL ) { + /* cleanup */ + return NULL; + } + p = begin - 1; + + submatch[ nsub - 1 ]->ls_map = map; + } + + /* + * Map with args ... + */ + } else if ( p[ 1 ] == '{' ) { + struct rewrite_map *map; + + map = rewrite_map_parse( info, p + 2, &begin ); + if ( map == NULL ) { + /* cleanup */ + return NULL; + } + p = begin - 1; + + /* + * Add a new value substitution scheme + */ + submatch = realloc( submatch, + sizeof( struct rewrite_submatch * )*( nsub + 1 ) ); + if ( submatch == NULL ) { + /* cleanup */ + return NULL; + } + submatch[ nsub ] = NULL; + submatch[ nsub - 1 ] = + calloc( sizeof( struct rewrite_submatch ), 1 ); + if ( submatch[ nsub - 1 ] == NULL ) { + /* cleanup */ + return NULL; + } + + submatch[ nsub - 1 ]->ls_type = + REWRITE_SUBMATCH_MAP_W_ARG; + + submatch[ nsub - 1 ]->ls_map = map; + } + } + + /* + * Last part of string + */ + subs = realloc( subs, sizeof( struct berval *)*( nsub + 2 ) ); + if ( subs == NULL ) { + /* + * XXX need to free the value subst stuff! + */ + free( submatch ); + return NULL; + } + + subs[ nsub + 1 ] = NULL; + l = p - begin; + if ( l > 0 ) { + subs[ nsub ] = calloc( sizeof( struct berval ), 1 ); + subs_len += l; + subs[ nsub ]->bv_len = l; + subs[ nsub ]->bv_val = malloc( l + 1 ); + strncpy( subs[ nsub ]->bv_val, begin, l ); + subs[ nsub ]->bv_val[ l ] = '\0'; + } else { + subs[ nsub ] = NULL; + } + + s = calloc( sizeof( struct rewrite_subst ), 1 ); + if ( s == NULL ) { + /* cleanup */ + return NULL; + } + + s->lt_subs_len = subs_len; + s->lt_subs = subs; + s->lt_num_submatch = nsub; + s->lt_submatch = submatch; + + return s; +} + +/* + * Copies the match referred to by submatch and fetched in string by match. + * Helper for rewrite_rule_apply. + */ +static int +submatch_copy( + struct rewrite_submatch *submatch, + const char *string, + const regmatch_t *match, + struct berval *val +) +{ + int c, l; + const char *s; + + assert( submatch != NULL ); + assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS + || submatch->ls_type == REWRITE_SUBMATCH_XMAP ); + assert( string != NULL ); + assert( match != NULL ); + assert( val != NULL ); + + c = submatch->ls_submatch; + s = string + match[ c ].rm_so; + l = match[ c ].rm_eo - match[ c ].rm_so; + + val->bv_val = NULL; + val->bv_len = l; + val->bv_val = calloc( sizeof( char ), l + 1 ); + if ( val->bv_val == NULL ) { + return REWRITE_ERR; + } + + strncpy( val->bv_val, s, l ); + val->bv_val[ l ] = '\0'; + + return REWRITE_SUCCESS; +} + +/* + * Substitutes a portion of rewritten string according to substitution + * pattern using submatches + */ +int +rewrite_subst_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_subst *subst, + const char *string, + const regmatch_t *match, + struct berval *val +) +{ + struct berval *submatch = NULL; + char *res = NULL; + int n, l, cl; + int rc = REWRITE_REGEXEC_OK; + + assert( info != NULL ); + assert( op != NULL ); + assert( subst != NULL ); + assert( string != NULL ); + assert( match != NULL ); + assert( val != NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + /* + * Prepare room for submatch expansion + */ + submatch = calloc( sizeof( struct berval ), + subst->lt_num_submatch ); + if ( submatch == NULL ) { + return REWRITE_REGEXEC_ERR; + } + + /* + * Resolve submatches (simple subst, map expansion and so). + */ + for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) { + struct berval key; + int rc; + + /* + * Get key + */ + switch( subst->lt_submatch[ n ]->ls_type ) { + case REWRITE_SUBMATCH_ASIS: + case REWRITE_SUBMATCH_XMAP: + rc = submatch_copy( subst->lt_submatch[ n ], + string, match, &key ); + if ( rc != REWRITE_SUCCESS ) { + free( submatch ); + return REWRITE_REGEXEC_ERR; + } + break; + + case REWRITE_SUBMATCH_MAP_W_ARG: + switch ( subst->lt_submatch[ n ]->ls_map->lm_type ) { + case REWRITE_MAP_GET_OP_VAR: + case REWRITE_MAP_GET_SESN_VAR: + case REWRITE_MAP_GET_PARAM: + rc = REWRITE_SUCCESS; + break; + default: + rc = rewrite_subst_apply( info, op, + subst->lt_submatch[ n ]->ls_map->lm_subst, + string, match, &key); + } + + if ( rc != REWRITE_SUCCESS ) { + free( submatch ); + return REWRITE_REGEXEC_ERR; + } + break; + + default: + Debug( LDAP_DEBUG_ANY, "Not Implemented\n%s%s%s", + "", "", "" ); + rc = REWRITE_ERR; + break; + } + + if ( rc != REWRITE_SUCCESS ) { + free( submatch ); + return REWRITE_REGEXEC_ERR; + } + + /* + * Resolve key + */ + switch ( subst->lt_submatch[ n ]->ls_type ) { + case REWRITE_SUBMATCH_ASIS: + submatch[ n ] = key; + rc = REWRITE_SUCCESS; + break; + + case REWRITE_SUBMATCH_XMAP: + rc = rewrite_xmap_apply( info, op, + subst->lt_submatch[ n ]->ls_map, + &key, &submatch[ n ] ); + break; + + case REWRITE_SUBMATCH_MAP_W_ARG: + rc = rewrite_map_apply( info, op, + subst->lt_submatch[ n ]->ls_map, + &key, &submatch[ n ] ); + break; + + default: + /* + * When implemented, this might return the + * exit status of a rewrite context, + * which may include a stop, or an + * unwilling to perform + */ + rc = REWRITE_ERR; + break; + } + + if ( rc != REWRITE_SUCCESS ) { + free( submatch ); + return REWRITE_REGEXEC_ERR; + } + + /* + * Increment the length of the resulting string + */ + l += submatch[ n ].bv_len; + } + + /* + * Alloc result buffer as big as the constant part + * of the subst pattern and initialize it + */ + l += subst->lt_subs_len; + res = calloc( sizeof( char ), l + 1 ); + if ( res == NULL ) { + free( submatch ); + return REWRITE_REGEXEC_ERR; + } + + /* + * Apply submatches (possibly resolved thru maps + */ + for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) { + if ( subst->lt_subs[ n ] != NULL ) { + strcpy( res + cl, subst->lt_subs[ n ]->bv_val); + cl += subst->lt_subs[ n ]->bv_len; + } + strcpy( res + cl, submatch[ n ].bv_val ); + cl += submatch[ n ].bv_len; + free( submatch[ n ].bv_val ); + } + if ( subst->lt_subs[ n ] != NULL ) { + strcpy( res + cl, subst->lt_subs[ n ]->bv_val ); + } + + val->bv_val = res; + val->bv_len = l; + + return rc; +} + diff --git a/libraries/librewrite/var.c b/libraries/librewrite/var.c new file mode 100644 index 0000000000..ca39ed13db --- /dev/null +++ b/libraries/librewrite/var.c @@ -0,0 +1,199 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ + +#include + +#include "rewrite-int.h" + +/* + * Compares two vars + */ +static int +rewrite_var_cmp( + const void *c1, + const void *c2 +) +{ + const struct rewrite_var *v1, *v2; + + v1 = ( struct rewrite_var * )c1; + v2 = ( struct rewrite_var * )c2; + + assert( v1 != NULL ); + assert( v2 != NULL ); + assert( v1->lv_name != NULL ); + assert( v2->lv_name != NULL ); + + return strcasecmp( v1->lv_name, v2->lv_name ); +} + +/* + * Duplicate var ? + */ +static int +rewrite_var_dup( + void *c1, + void *c2 +) +{ + struct rewrite_var *v1, *v2; + + v1 = ( struct rewrite_var * )c1; + v2 = ( struct rewrite_var * )c2; + + assert( v1 != NULL ); + assert( v2 != NULL ); + assert( v1->lv_name != NULL ); + assert( v2->lv_name != NULL ); + + return ( strcasecmp( v1->lv_name, v2->lv_name ) == 0 ? -1 : 0 ); +} + +/* + * Finds a var + */ +struct rewrite_var * +rewrite_var_find( + Avlnode *tree, + const char *name +) +{ + struct rewrite_var var; + + assert( name != NULL ); + + var.lv_name = ( char * )name; + return ( struct rewrite_var * )avl_find( tree, + ( caddr_t )&var, rewrite_var_cmp ); +} + +/* + * Inserts a newly created var + */ +struct rewrite_var * +rewrite_var_insert( + Avlnode **tree, + const char *name, + const char *value +) +{ + struct rewrite_var *var; + int rc; + + assert( tree != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + var = calloc( sizeof( struct rewrite_var ), 1 ); + if ( var == NULL ) { + return NULL; + } + var->lv_name = ( char * )strdup( name ); + if ( var->lv_name == NULL ) { + free( var ); + return NULL; + } + var->lv_value.bv_val = strdup( value ); + if ( var->lv_value.bv_val == NULL ) { + free( var ); + free( var->lv_name ); + return NULL; + } + var->lv_value.bv_len = strlen( value ); + rc = avl_insert( tree, ( caddr_t )var, + rewrite_var_cmp, rewrite_var_dup ); + if ( rc != 0 ) { + free( var ); + free( var->lv_name ); + free( var->lv_value.bv_val ); + return NULL; + } + + return var; +} + +/* + * Sets/inserts a var + */ +struct rewrite_var * +rewrite_var_set( + Avlnode **tree, + const char *name, + const char *value, + int insert +) +{ + struct rewrite_var *var; + + assert( tree != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + var = rewrite_var_find( *tree, name ); + if ( var == NULL ) { + if ( insert ) { + return rewrite_var_insert( tree, name, value ); + } else { + return NULL; + } + } else { + assert( var->lv_value.bv_val != NULL ); + + free( var->lv_value.bv_val ); + var->lv_value.bv_val = ( char * )value; + var->lv_value.bv_len = strlen( value ); + } + + return var; +} + +/* + * Frees a var + */ +static void +rewrite_var_free( + struct rewrite_var *var +) +{ + assert( var != NULL ); + + assert( var->lv_name != NULL ); + assert( var->lv_value.bv_val != NULL ); + + free( var->lv_name ); + free( var->lv_value.bv_val ); +} + +/* + * Deletes a var tree + */ +int +rewrite_var_delete( + Avlnode *tree +) +{ + avl_free( tree, ( AVL_FREE )rewrite_var_free ); + return REWRITE_SUCCESS; +} + diff --git a/servers/slapd/back-ldap/add.c b/servers/slapd/back-ldap/add.c index de1b4c1a97..cccc79e2ba 100644 --- a/servers/slapd/back-ldap/add.c +++ b/servers/slapd/back-ldap/add.c @@ -58,17 +58,36 @@ ldap_back_add( int i; Attribute *a; LDAPMod **attrs; - char *mdn, *mapped; + char *mdn = NULL, *mapped; lc = ldap_back_getconn(li, conn, op); if ( !lc || !ldap_back_dobind( lc, op ) ) { return( -1 ); } - mdn = ldap_back_dn_massage( li, ch_strdup( e->e_dn ), 0 ); + /* + * Rewrite the add dn, if needed + */ +#ifdef ENABLE_REWRITE + switch (rewrite_session( li->rwinfo, "addDn", e->e_dn, conn, &mdn )) { + case REWRITE_REGEXEC_OK: if ( mdn == NULL ) { + mdn = e->e_dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> addDn: \"%s\" -> \"%s\"\n%s", + e->e_dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: return( -1 ); } +#else /* !ENABLE_REWRITE */ + mdn = ldap_back_dn_massage( li, ch_strdup( e->e_dn ), 0 ); +#endif /* !ENABLE_REWRITE */ /* Count number of attributes in entry */ for (i = 1, a = e->e_attrs; a; i++, a = a->a_next) @@ -78,16 +97,52 @@ ldap_back_add( attrs = (LDAPMod **)ch_malloc(sizeof(LDAPMod *)*i); for (i=0, a=e->e_attrs; a; a=a->a_next) { + /* + * lastmod should always be , so that + * creation/modification operational attrs + * of the target directory are used, if available + */ +#if 0 + if ( !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_creatorsName->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_createTimestamp->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_modifiersName->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_modifyTimestamp->ad_cname->bv_val ) + ) { + continue; + } +#endif + mapped = ldap_back_map(&li->at_map, a->a_desc->ad_cname->bv_val, 0); - if (mapped != NULL) { - attrs[i] = (LDAPMod *)ch_malloc(sizeof(LDAPMod)); - if (attrs[i] != NULL) { - attrs[i]->mod_op = LDAP_MOD_BVALUES; - attrs[i]->mod_type = mapped; - attrs[i]->mod_vals.modv_bvals = a->a_vals; - i++; - } + if (mapped == NULL) { + continue; + } + + attrs[i] = (LDAPMod *)ch_malloc(sizeof(LDAPMod)); + if (attrs[i] == NULL) { + continue; + } + + attrs[i]->mod_op = LDAP_MOD_BVALUES; + attrs[i]->mod_type = mapped; + +#ifdef ENABLE_REWRITE + /* + * FIXME: dn-valued attrs should be rewritten + * to allow their use in ACLs at the back-ldap + * level. + */ + if ( strcmp( a->a_desc->ad_type->sat_syntax->ssyn_oid, + SLAPD_DN_SYNTAX ) == 0 ) { + ldap_dnattr_rewrite( li->rwinfo, a->a_vals, conn ); } +#endif /* ENABLE_REWRITE */ + + attrs[i]->mod_vals.modv_bvals = a->a_vals; + i++; } attrs[i] = NULL; @@ -95,6 +150,60 @@ ldap_back_add( for (--i; i>= 0; --i) free(attrs[i]); free(attrs); +#ifdef ENABLE_REWRITE + if ( mdn != e->e_dn ) { +#endif /* ENABLE_REWRITE */ free( mdn ); - return( ldap_back_op_result( lc, op )); +#ifdef ENABLE_REWRITE + } +#endif /* ENABLE_REWRITE */ + + return( ldap_back_op_result( lc, op ) ); +} + +#ifdef ENABLE_REWRITE +int +ldap_dnattr_rewrite( + struct rewrite_info *rwinfo, + struct berval **a_vals, + void *cookie +) +{ + int j; + char *mattr; + + for ( j = 0; a_vals[ j ] != NULL; j++ ) { + switch ( rewrite_session( rwinfo, "bindDn", a_vals[ j ]->bv_val, + cookie, &mattr )) { + case REWRITE_REGEXEC_OK: + if ( mattr == NULL ) { + /* no substitution */ + continue; + } + Debug( LDAP_DEBUG_ARGS, + "rw> bindDn (in add of dn-valued attr):" + " \"%s\" -> \"%s\"\n%s", + a_vals[ j ]->bv_val, mattr, "" ); + + free( a_vals[ j ]->bv_val ); + a_vals[ j ]->bv_val = mattr; + a_vals[ j ]->bv_len = strlen( mattr ); + + break; + + case REWRITE_REGEXEC_UNWILLING: + + case REWRITE_REGEXEC_ERR: + /* + * FIXME: better give up, + * skip the attribute + * or leave it untouched? + */ + break; + } + } + + return 0; } +#endif /* ENABLE_REWRITE */ + diff --git a/servers/slapd/back-ldap/back-ldap.h b/servers/slapd/back-ldap/back-ldap.h index aafb09f794..45fb33bea6 100644 --- a/servers/slapd/back-ldap/back-ldap.h +++ b/servers/slapd/back-ldap/back-ldap.h @@ -40,6 +40,11 @@ #include "external.h" +/* String rewrite library */ +#ifdef ENABLE_REWRITE +#include "rewrite.h" +#endif /* ENABLE_REWRITE */ + LDAP_BEGIN_DECL struct slap_conn; @@ -66,14 +71,15 @@ struct ldapmapping { struct ldapinfo { char *url; -#if 0 /* unused! */ - char *suffix; -#endif /* 0 */ - char **suffix_massage; char *binddn; char *bindpw; ldap_pvt_thread_mutex_t conn_mutex; Avlnode *conntree; +#ifdef ENABLE_REWRITE + struct rewrite_info *rwinfo; +#else /* !ENABLE_REWRITE */ + char **suffix_massage; +#endif /* !ENABLE_REWRITE */ struct ldapmap oc_map; struct ldapmap at_map; @@ -89,15 +95,33 @@ int back_ldap_LTX_init_module(int argc, char *argv[]); char *ldap_back_dn_massage(struct ldapinfo *li, char *dn, int normalized); char *ldap_back_dn_restore(struct ldapinfo *li, char *dn, int normalized); -int conn_cmp(const void *, const void *); -int conn_dup(void *, void *); +extern int ldap_back_conn_cmp( const void *c1, const void *c2); +extern int ldap_back_conn_dup( void *c1, void *c2 ); int mapping_cmp (const void *, const void *); int mapping_dup (void *, void *); char *ldap_back_map ( struct ldapmap *map, char *s, int remap ); -char *ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap ); -char **ldap_back_map_attrs ( struct ldapinfo *li, char **a, int remap ); +char * +ldap_back_map_filter( + struct ldapmap *at_map, + struct ldapmap *oc_map, + char *f, + int remap +); +char ** +ldap_back_map_attrs( + struct ldapmap *at_map, + char **a, + int remap +); + +extern void mapping_free ( struct ldapmapping *mapping ); + +#ifdef ENABLE_REWRITE +extern int suffix_massage_config( struct rewrite_info *info, int argc, char **argv ); +extern int ldap_dnattr_rewrite( struct rewrite_info *rwinfo, struct berval **a_vals, void *cookie ); +#endif /* ENABLE_REWRITE */ LDAP_END_DECL diff --git a/servers/slapd/back-ldap/bind.c b/servers/slapd/back-ldap/bind.c index 89ef3f517b..0a6c7aed00 100644 --- a/servers/slapd/back-ldap/bind.c +++ b/servers/slapd/back-ldap/bind.c @@ -47,6 +47,8 @@ #include "slap.h" #include "back-ldap.h" +#define PRINT_CONNTREE 0 + int ldap_back_bind( Backend *be, @@ -72,30 +74,56 @@ ldap_back_bind( return( -1 ); } - mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); - if ( mdn == NULL ) { - return -1; + /* + * Rewrite the bind dn if needed + */ +#ifdef ENABLE_REWRITE + switch ( rewrite_session( li->rwinfo, "bindDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> bindDn: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return( -1 ); } +#else /* !ENABLE_REWRITE */ + mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); +#endif /* !ENABLE_REWRITE */ - if (ldap_bind_s(lc->ld, mdn, cred->bv_val, method) != LDAP_SUCCESS) { + rc = ldap_bind_s(lc->ld, mdn, cred->bv_val, method); + if (rc != LDAP_SUCCESS) { rc = ldap_back_op_result( lc, op ); } else { lc->bound = 1; } - + +#ifdef ENABLE_REWRITE + if ( mdn != dn ) { +#endif /* ENABLE_REWRITE */ free( mdn ); +#ifdef ENABLE_REWRITE + } +#endif /* ENABLE_REWRITE */ return( rc ); } /* - * conn_cmp + * ldap_back_conn_cmp * * compares two struct ldapconn based on the value of the conn pointer; * used by avl stuff */ int -conn_cmp( +ldap_back_conn_cmp( const void *c1, const void *c2 ) @@ -107,13 +135,13 @@ conn_cmp( } /* - * conn_dup + * ldap_back_conn_dup * * returns -1 in case a duplicate struct ldapconn has been inserted; * used by avl stuff */ int -conn_dup( +ldap_back_conn_dup( void *c1, void *c2 ) @@ -124,6 +152,7 @@ conn_dup( return( ( lc1->conn == lc2->conn ) ? -1 : 0 ); } +#if PRINT_CONNTREE > 0 static void ravl_print( Avlnode *root, int depth ) { int i; @@ -136,7 +165,7 @@ static void ravl_print( Avlnode *root, int depth ) for ( i = 0; i < depth; i++ ) printf( " " ); - printf( "c(%d) %d\n", ((struct ldapconn *) root->avl_data)->conn->c_connid, root->avl_bf ); + printf( "c(%ld) %d\n", ((struct ldapconn *) root->avl_data)->conn->c_connid, root->avl_bf ); ravl_print( root->avl_left, depth+1 ); } @@ -153,6 +182,7 @@ static void myprint( Avlnode *root ) printf( "********\n" ); } +#endif /* PRINT_CONNTREE */ struct ldapconn * ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op) @@ -164,7 +194,7 @@ ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op) lc_curr.conn = conn; ldap_pvt_thread_mutex_lock( &li->conn_mutex ); lc = (struct ldapconn *)avl_find( li->conntree, - (caddr_t)&lc_curr, conn_cmp ); + (caddr_t)&lc_curr, ldap_back_conn_cmp ); ldap_pvt_thread_mutex_unlock( &li->conn_mutex ); /* Looks like we didn't get a bind. Open a new session... */ @@ -186,9 +216,49 @@ ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op) lc = (struct ldapconn *)ch_malloc(sizeof(struct ldapconn)); lc->conn = conn; lc->ld = ld; + +#ifdef ENABLE_REWRITE + /* + * Sets a cookie for the rewrite session + */ + ( void )rewrite_session_init( li->rwinfo, conn ); +#endif /* ENABLE_REWRITE */ + if ( lc->conn->c_cdn != NULL && lc->conn->c_cdn[0] != '\0' ) { + + /* + * Rewrite the bind dn if needed + */ +#ifdef ENABLE_REWRITE + lc->bound_dn = NULL; + switch ( rewrite_session( li->rwinfo, "bindDn", + lc->conn->c_cdn, conn, + &lc->bound_dn ) ) { + case REWRITE_REGEXEC_OK: + if ( lc->bound_dn == NULL ) { + lc->bound_dn = + ch_strdup( lc->conn->c_cdn ); + } + Debug( LDAP_DEBUG_ARGS, + "rw> bindDn: \"%s\" ->" + " \"%s\"\n%s", + lc->conn->c_cdn, + lc->bound_dn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, + LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return( NULL ); + } +#else /* !ENABLE_REWRITE */ lc->bound_dn = ldap_back_dn_massage( li, - ch_strdup( lc->conn->c_cdn ), 0 ); + ch_strdup( lc->conn->c_cdn ), 0 ); +#endif /* !ENABLE_REWRITE */ } else { lc->bound_dn = NULL; } @@ -197,17 +267,17 @@ ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op) /* Inserts the newly created ldapconn in the avl tree */ ldap_pvt_thread_mutex_lock( &li->conn_mutex ); err = avl_insert( &li->conntree, (caddr_t)lc, - conn_cmp, conn_dup ); + ldap_back_conn_cmp, ldap_back_conn_dup ); -#if 0 +#if PRINT_CONNTREE > 0 myprint( li->conntree ); -#endif +#endif /* PRINT_CONNTREE */ ldap_pvt_thread_mutex_unlock( &li->conn_mutex ); Debug( LDAP_DEBUG_TRACE, - "=>ldap_back_getconn: conn %d inserted\n", - lc->conn->c_connid, 0, 0 ); + "=>ldap_back_getconn: conn %ld inserted%s%s\n", + lc->conn->c_connid, "", "" ); /* Err could be -1 in case a duplicate ldapconn is inserted */ if ( err != 0 ) { @@ -218,8 +288,8 @@ ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op) } } else { Debug( LDAP_DEBUG_TRACE, - "=>ldap_back_getconn: conn %d fetched\n", - lc->conn->c_connid, 0, 0 ); + "=>ldap_back_getconn: conn %ld fetched%s%s\n", + lc->conn->c_connid, "", "" ); } return( lc ); @@ -305,9 +375,23 @@ ldap_back_op_result(struct ldapconn *lc, Operation *op) ldap_get_option(lc->ld, LDAP_OPT_ERROR_STRING, &msg); ldap_get_option(lc->ld, LDAP_OPT_MATCHED_DN, &match); err = ldap_back_map_result(err); + +#ifdef ENABLE_REWRITE + + /* + * need rewrite info; mmmh ... + */ + +#else /* !ENABLE_REWRITE */ + send_ldap_result( lc->conn, op, err, match, msg, NULL, NULL ); /* better test the pointers before freeing? */ - if ( match ) free( match ); + if ( match ) { + free( match ); + } +#endif /* !ENABLE_REWRITE */ + if ( msg ) free( msg ); return( (err==LDAP_SUCCESS) ? 0 : -1 ); } + diff --git a/servers/slapd/back-ldap/compare.c b/servers/slapd/back-ldap/compare.c index c8033cc164..99bd397243 100644 --- a/servers/slapd/back-ldap/compare.c +++ b/servers/slapd/back-ldap/compare.c @@ -64,10 +64,32 @@ ldap_back_compare( return( -1 ); } + /* + * Rewrite the compare dn, if needed + */ +#ifdef ENABLE_REWRITE + switch ( rewrite_session( li->rwinfo, "compareDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> compareDn: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return( -1 ); + } +#else /* !ENABLE_REWRITE */ mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); - if ( mdn == NULL ) { - return -1; - } + if ( mdn == NULL ) { + return -1; + } +#endif /* !ENABLE_REWRITE */ mapped_oc = ldap_back_map(&li->oc_map, ava->aa_desc->ad_cname->bv_val, 0); if (mapped_oc == NULL) @@ -79,7 +101,13 @@ ldap_back_compare( ldap_compare_s( lc->ld, mdn, mapped_oc, mapped_at ); - free( mdn ); +#ifdef ENABLE_REWRITE + if ( mdn != dn ) { +#endif /* ENABLE_REWRITE */ + free( mdn ); +#ifdef ENABLE_REWRITE + } +#endif /* ENABLE_REWRITE */ return( ldap_back_op_result( lc, op ) ); } diff --git a/servers/slapd/back-ldap/config.c b/servers/slapd/back-ldap/config.c index 5fb1f3a6c1..f493b0d8e5 100644 --- a/servers/slapd/back-ldap/config.c +++ b/servers/slapd/back-ldap/config.c @@ -113,34 +113,59 @@ ldap_back_db_config( /* dn massaging */ } else if ( strcasecmp( argv[0], "suffixmassage" ) == 0 ) { +#ifndef ENABLE_REWRITE char *dn, *massaged_dn; +#endif /* ENABLE_REWRITE */ BackendDB *tmp_be; + /* + * syntax: + * + * suffixmassage + * + * the field must be defined as a valid suffix + * (or suffixAlias?) for the current database; + * the shouldn't have already been + * defined as a valid suffix or suffixAlias for the + * current server + */ if ( argc != 3 ) { - fprintf( stderr, - "%s: line %d: syntax is \"suffixMassage \"\n", + fprintf( stderr, "%s: line %d: syntax is" + " \"suffixMassage " + " \"\n", fname, lineno ); return( 1 ); } tmp_be = select_backend( argv[1], 0 ); if ( tmp_be != NULL && tmp_be != be ) { - fprintf( stderr, - "%s: line %d: suffix already in use by another backend in" - " \"suffixMassage \"\n", + fprintf( stderr, "%s: line %d: suffix already in use" + " by another backend in" + " \"suffixMassage " + " \"\n", fname, lineno ); return( 1 ); } tmp_be = select_backend( argv[2], 0 ); if ( tmp_be != NULL ) { - fprintf( stderr, - "%s: line %d: massaged suffix already in use by another backend in" - " \"suffixMassage \"\n", + fprintf( stderr, "%s: line %d: massaged suffix" + " already in use by another backend in" + " \"suffixMassage " + " \"\n", fname, lineno ); return( 1 ); } - + +#ifdef ENABLE_REWRITE + /* + * The suffix massaging is emulated by means of the + * rewrite capabilities + * FIXME: no extra rewrite capabilities should be added + * to the database + */ + return suffix_massage_config( li->rwinfo, argc, argv ); +#else /* !ENABLE_REWRITE */ dn = ch_strdup( argv[1] ); charray_add( &li->suffix_massage, dn ); (void) dn_normalize( dn ); @@ -153,7 +178,14 @@ ldap_back_db_config( free( dn ); free( massaged_dn ); +#endif /* !ENABLE_REWRITE */ +#ifdef ENABLE_REWRITE + /* rewrite stuff ... */ + } else if ( strncasecmp( argv[0], "rewrite", 7 ) == 0 ) { + return rewrite_parse( li->rwinfo, fname, lineno, argc, argv ); +#endif /* ENABLE_REWRITE */ + /* objectclass/attribute mapping */ } else if ( strcasecmp( argv[0], "map" ) == 0 ) { struct ldapmap *map; @@ -293,7 +325,12 @@ ldap_back_map ( struct ldapmap *map, char *s, int remap ) } char * -ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap ) +ldap_back_map_filter( + struct ldapmap *at_map, + struct ldapmap *oc_map, + char *f, + int remap +) { char *nf, *m, *p, *q, *s, c; int len, extra, plen, in_quote; @@ -315,7 +352,7 @@ ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap ) s = nf; q = NULL; in_quote = 0; - for (p = f; c = *p; p++) { + for (p = f; (c = *p); p++) { if (c == '"') { in_quote = !in_quote; if (q != NULL) { @@ -339,9 +376,9 @@ ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap ) } else { if (q != NULL) { *p = 0; - m = ldap_back_map(&li->at_map, q, remap); + m = ldap_back_map(at_map, q, remap); if (m == NULL) - m = ldap_back_map(&li->oc_map, q, remap); + m = ldap_back_map(oc_map, q, remap); if (m == NULL) { m = q; } @@ -374,7 +411,11 @@ ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap ) } char ** -ldap_back_map_attrs ( struct ldapinfo *li, char **a, int remap ) +ldap_back_map_attrs( + struct ldapmap *at_map, + char **a, + int remap +) { int i, j, count; char **na, *mapped; @@ -391,7 +432,7 @@ ldap_back_map_attrs ( struct ldapinfo *li, char **a, int remap ) return(NULL); for (i = 0, j = 0; i < count; i++) { - mapped = ldap_back_map(&li->at_map, a[i], remap); + mapped = ldap_back_map(at_map, a[i], remap); if (mapped != NULL) { mapped = ch_strdup(mapped); if (mapped == NULL) { @@ -405,3 +446,137 @@ ldap_back_map_attrs ( struct ldapinfo *li, char **a, int remap ) return(na); } +#ifdef ENABLE_REWRITE +static char * +suffix_massage_regexize( const char *s ) +{ + char *res, *p, *r; + int i; + + for ( i = 0, p = ( char * )s; + ( r = strchr( p, ',' ) ) != NULL; + p = r + 1, i++ ) + ; + + res = ch_calloc( sizeof( char ), strlen( s ) + 4 + 4*i + 1 ); + + strcpy( res, "(.*)" ); + for ( i = 0, p = ( char * )s; + ( r = strchr( p, ',' ) ) != NULL; + p = r + 1 , i++ ) { + strncat( res, p, r - p + 1 ); + strcat( res, "[ ]?" ); + + if ( r[ 1 ] == ' ' ) { + r++; + } + } + strcat( res, p ); + + return res; +} + +static char * +suffix_massage_patternize( const char *s, int normalize ) +{ + char *res; + + res = ch_calloc( sizeof( char ), strlen( s ) + 3 ); + + sprintf( res, "%%1%s", s ); + + if ( normalize ) { + char *out = dn_normalize( res + 2 ); + if ( out != res + 2 ) { + strcpy( res + 2, out ); + free( out ); + } + } + + return res; +} + +int +suffix_massage_config( + struct rewrite_info *info, + int argc, + char **argv +) +{ + char *rargv[ 5 ]; + + rargv[ 0 ] = "rewriteEngine"; + rargv[ 1 ] = "on"; + rargv[ 2 ] = NULL; + rewrite_parse( info, "", 1, 2, rargv ); + + rargv[ 0 ] = "rewriteContext"; + rargv[ 1 ] = "default"; + rargv[ 2 ] = NULL; + rewrite_parse( info, "", 2, 2, rargv ); + + rargv[ 0 ] = "rewriteRule"; + rargv[ 1 ] = suffix_massage_regexize( argv[ 1 ] ); + rargv[ 2 ] = suffix_massage_patternize( argv[ 2 ], 0 ); + rargv[ 3 ] = ":"; + rargv[ 4 ] = NULL; + rewrite_parse( info, "", 3, 4, rargv ); + ch_free( rargv[ 1 ] ); + ch_free( rargv[ 2 ] ); + + rargv[ 0 ] = "rewriteContext"; + rargv[ 1 ] = "searchResult"; + rargv[ 2 ] = NULL; + rewrite_parse( info, "", 4, 2, rargv ); + + rargv[ 0 ] = "rewriteRule"; + rargv[ 1 ] = suffix_massage_regexize( argv[ 2 ] ); + rargv[ 2 ] = suffix_massage_patternize( argv[ 1 ], 0 ); + rargv[ 3 ] = ":"; + rargv[ 4 ] = NULL; + rewrite_parse( info, "", 5, 4, rargv ); + ch_free( rargv[ 1 ] ); + ch_free( rargv[ 2 ] ); + + /* + * the filter should be rewritten as + * + * rewriteRule + * "(.*)member=([^)]+),o=Foo Bar,[ ]?c=US(.*)" + * "%1member=%2,dc=example,dc=com%3" + * + * where "o=Foo Bar, c=US" is the virtual naming context, + * and "dc=example, dc=com" is the real naming context + */ + rargv[ 0 ] = "rewriteContext"; + rargv[ 1 ] = "searchFilter"; + rargv[ 2 ] = NULL; + rewrite_parse( info, "", 6, 2, rargv ); + +#if 0 /* matched is not normalized */ + rargv[ 0 ] = "rewriteContext"; + rargv[ 1 ] = "matchedDn"; + rargv[ 2 ] = "alias"; + rargv[ 3 ] = "searchResult"; + rargv[ 4 ] = NULL; + rewrite_parse( info, "", 7, 4, rargv ); +#else /* normalize matched */ + rargv[ 0 ] = "rewriteContext"; + rargv[ 1 ] = "matchedDn"; + rargv[ 2 ] = NULL; + rewrite_parse( info, "", 7, 2, rargv ); + + rargv[ 0 ] = "rewriteRule"; + rargv[ 1 ] = suffix_massage_regexize( argv[ 2 ] ); + rargv[ 2 ] = suffix_massage_patternize( argv[ 1 ], 1 ); + rargv[ 3 ] = ":"; + rargv[ 4 ] = NULL; + rewrite_parse( info, "", 8, 4, rargv ); + ch_free( rargv[ 1 ] ); + ch_free( rargv[ 2 ] ); +#endif /* normalize matched */ + + return 0; +} +#endif /* ENABLE_REWRITE */ + diff --git a/servers/slapd/back-ldap/delete.c b/servers/slapd/back-ldap/delete.c index 4b92412be1..ef1d947518 100644 --- a/servers/slapd/back-ldap/delete.c +++ b/servers/slapd/back-ldap/delete.c @@ -57,7 +57,7 @@ ldap_back_delete( struct ldapinfo *li = (struct ldapinfo *) be->be_private; struct ldapconn *lc; - char *mdn; + char *mdn = NULL; lc = ldap_back_getconn( li, conn, op ); @@ -65,14 +65,39 @@ ldap_back_delete( return( -1 ); } - mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); + /* + * Rewrite the compare dn, if needed + */ +#ifdef ENABLE_REWRITE + switch ( rewrite_session( li->rwinfo, "deleteDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> deleteDn: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: return( -1 ); } +#else /* !ENABLE_REWRITE */ + mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); +#endif /* !ENABLE_REWRITE */ ldap_delete_s( lc->ld, mdn ); - free( mdn ); +#ifdef ENABLE_REWRITE + if ( mdn != dn ) { +#endif /* ENABLE_REWRITE */ + free( mdn ); +#ifdef ENABLE_REWRITE + } +#endif /* ENABLE_REWRITE */ return( ldap_back_op_result( lc, op ) ); } diff --git a/servers/slapd/back-ldap/group.c b/servers/slapd/back-ldap/group.c index 52d10b3164..800b873e8d 100644 --- a/servers/slapd/back-ldap/group.c +++ b/servers/slapd/back-ldap/group.c @@ -16,9 +16,9 @@ #include "back-ldap.h" -/* return 0 IFF op_dn is a value in member attribute +/* return 0 IFF op_dn is a value in group_at (member) attribute * of entry with gr_dn AND that entry has an objectClass - * value of groupOfNames + * value of group_oc (groupOfNames) */ int ldap_back_group( @@ -35,12 +35,13 @@ ldap_back_group( struct ldapinfo *li = (struct ldapinfo *) be->be_private; int rc = 1; Attribute *attr; - Entry *e; struct berval bv; + LDAPMessage *result; char *gattr[2]; char *filter; LDAP *ld; + char *mop_ndn, *mgr_ndn; AttributeDescription *ad_objectClass = slap_schema.si_ad_objectClass; char *group_oc_name = NULL; @@ -55,65 +56,150 @@ ldap_back_group( if (target != NULL && strcmp(target->e_ndn, gr_ndn) == 0) { /* we already have a copy of the entry */ /* attribute and objectclass mapping has already been done */ - e = target; - if( is_entry_objectclass( e, group_oc ) ) { - return(1); + /* + * first we need to check if the objectClass attribute + * has been retieved; otherwise we need to repeat the search + */ + attr = attr_find( target->e_attrs, ad_objectClass ); + if ( attr != NULL ) { + + /* + * Now we can check for the group objectClass value + */ + if( !is_entry_objectclass( target, group_oc ) ) { + return(1); + } + + /* + * This part has been reworked: the group attr compare + * fails only if the attribute is PRESENT but the value + * is NOT PRESENT; if the attribute is NOT PRESENT, the + * search must be repeated as well. + * This may happen if a search for an entry has already + * been performed (target is not null) but the group + * attribute has not been required + */ + if ((attr = attr_find(target->e_attrs, group_at)) != NULL) { + bv.bv_val = (char *) op_ndn; + bv.bv_len = strlen( op_ndn ); + if( value_find( group_at, attr->a_vals, &bv ) != LDAP_SUCCESS ) + return(1); + return(0); + } /* else: repeat the search */ + } /* else: repeat the search */ + } /* else: do the search */ + + /* + * Rewrite the op ndn if needed + */ +#ifdef ENABLE_REWRITE + switch ( rewrite_session( li->rwinfo, "bindDn", + op_ndn, conn, &mop_ndn ) ) { + case REWRITE_REGEXEC_OK: + if ( mop_ndn == NULL ) { + mop_ndn = ( char * )op_ndn; } + Debug( LDAP_DEBUG_ARGS, + "rw> bindDn (op ndn in group): \"%s\" -> \"%s\"\n%s", + op_ndn, mop_ndn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + + case REWRITE_REGEXEC_ERR: + goto cleanup; + } - if ((attr = attr_find(e->e_attrs, group_at)) == NULL) - return(1); + /* + * Rewrite the gr ndn if needed + */ + switch ( rewrite_session( li->rwinfo, "searchBase", + gr_ndn, conn, &mgr_ndn ) ) { + case REWRITE_REGEXEC_OK: + if ( mgr_ndn == NULL ) { + mgr_ndn = ( char * )gr_ndn; + } + Debug( LDAP_DEBUG_ARGS, + "rw> searchBase (gr ndn in group):" + " \"%s\" -> \"%s\"\n%s", + gr_ndn, mgr_ndn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + + case REWRITE_REGEXEC_ERR: + goto cleanup; + } +#else /* !ENABLE_REWRITE */ + mop_ndn = ldap_back_dn_massage( li, ch_strdup( op_ndn ), 1 ); + if ( mop_ndn == NULL ) { + goto cleanup; + } + mgr_ndn = ldap_back_dn_massage( li, ch_strdup( gr_ndn ), 1 ); + if ( mgr_ndn == NULL ) { + goto cleanup; + } +#endif /* !ENABLE_REWRITE */ + + group_oc_name = ldap_back_map(&li->oc_map, group_oc_name, 0); + if (group_oc_name == NULL) + goto cleanup; + group_at_name = ldap_back_map(&li->at_map, group_at_name, 0); + if (group_at_name == NULL) + goto cleanup; + + filter = ch_malloc(sizeof("(&(objectclass=)(=))") + + strlen(group_oc_name) + + strlen(group_at_name) + + strlen(mop_ndn) + 1); + if (filter == NULL) + goto cleanup; + + if (ldap_initialize(&ld, li->url) != LDAP_SUCCESS) { + goto cleanup; + } - bv.bv_val = (char *) op_ndn; - bv.bv_len = strlen( op_ndn ); - if( value_find( group_at, attr->a_vals, &bv ) == 0 ) - return(1); + if (ldap_bind_s(ld, li->binddn, li->bindpw, LDAP_AUTH_SIMPLE) + != LDAP_SUCCESS) { + goto cleanup; + } - } else { - group_oc_name = ldap_back_map(&li->oc_map, group_oc_name, 0); - if (group_oc_name == NULL) - return(1); - group_at_name = ldap_back_map(&li->at_map, group_at_name, 0); - if (group_at_name == NULL) - return(1); - - filter = ch_malloc(sizeof("(&(objectclass=)(=))") - + strlen(group_oc_name) - + strlen(group_at_name) - + strlen(op_ndn) + 1); - if (filter == NULL) - return(1); - - if (ldap_initialize(&ld, li->url) != LDAP_SUCCESS) { - ch_free(filter); - return(1); - } + strcpy(filter, "(&(objectclass="); + strcat(filter, group_oc_name); + strcat(filter, ")("); + strcat(filter, group_at_name); + strcat(filter, "="); + strcat(filter, mop_ndn); + strcat(filter, "))"); + + gattr[0] = "objectclass"; + gattr[1] = NULL; + if (ldap_search_ext_s(ld, mgr_ndn, LDAP_SCOPE_BASE, filter, + gattr, 0, NULL, NULL, LDAP_NO_LIMIT, + LDAP_NO_LIMIT, &result) == LDAP_SUCCESS) { + if (ldap_first_entry(ld, result) != NULL) + rc = 0; + ldap_msgfree(result); + } - if (ldap_bind_s(ld, li->binddn, li->bindpw, LDAP_AUTH_SIMPLE) == LDAP_SUCCESS) { - strcpy(filter, "(&(objectclass="); - strcat(filter, group_oc_name); - strcat(filter, ")("); - strcat(filter, group_at_name); - strcat(filter, "="); - strcat(filter, op_ndn); - strcat(filter, "))"); - - gattr[0] = "objectclass"; - gattr[1] = NULL; - if (ldap_search_ext_s(ld, gr_ndn, LDAP_SCOPE_BASE, filter, - gattr, 0, NULL, NULL, LDAP_NO_LIMIT, - LDAP_NO_LIMIT, &result) == LDAP_SUCCESS) - { - if (ldap_first_entry(ld, result) != NULL) - rc = 0; - ldap_msgfree(result); - } - } +cleanup: + if ( ld != NULL ) { ldap_unbind(ld); - ch_free(filter); - return(rc); - } - - return(0); + } + ch_free(filter); +#ifdef ENABLE_REWRITE + if ( mop_ndn != op_ndn ) { +#endif /* ENABLE_REWRITE */ + free( mop_ndn ); +#ifdef ENABLE_REWRITE + } + if ( mgr_ndn != gr_ndn ) { +#endif /* ENABLE_REWRITE */ + free( mgr_ndn ); +#ifdef ENABLE_REWRITE + } +#endif /* ENABLE_REWRITE */ + return(rc); } diff --git a/servers/slapd/back-ldap/init.c b/servers/slapd/back-ldap/init.c index 96e1acbd79..4b4c6975d6 100644 --- a/servers/slapd/back-ldap/init.c +++ b/servers/slapd/back-ldap/init.c @@ -106,6 +106,18 @@ ldap_back_db_init( struct ldapmapping *mapping; li = (struct ldapinfo *) ch_calloc( 1, sizeof(struct ldapinfo) ); + if ( li == NULL ) { + return -1; + } + +#ifdef ENABLE_REWRITE + li->rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT ); + if ( li->rwinfo == NULL ) { + ch_free( li ); + return -1; + } +#endif /* ENABLE_REWRITE */ + ldap_pvt_thread_mutex_init( &li->conn_mutex ); mapping = (struct ldapmapping *)ch_calloc( 2, sizeof(struct ldapmapping) ); @@ -123,7 +135,7 @@ ldap_back_db_init( be->be_private = li; - return li == NULL; + return 0; } static void @@ -136,7 +148,7 @@ conn_free( free( lc ); } -static void +void mapping_free ( struct ldapmapping *mapping ) { ch_free( mapping->src ); @@ -168,13 +180,18 @@ ldap_back_db_destroy( free(li->bindpw); li->bindpw = NULL; } - if (li->suffix_massage) { - ldap_value_free( li->suffix_massage ); - li->suffix_massage = NULL; - } if (li->conntree) { avl_free( li->conntree, (AVL_FREE) conn_free ); } +#ifdef ENABLE_REWRITE + if (li->rwinfo) { + rewrite_info_delete( li->rwinfo ); + } +#else /* !ENABLE_REWRITE */ + if (li->suffix_massage) { + ldap_value_free( li->suffix_massage ); + } +#endif /* !ENABLE_REWRITE */ avl_free( li->oc_map.remap, NULL ); avl_free( li->oc_map.map, (AVL_FREE) mapping_free ); diff --git a/servers/slapd/back-ldap/modify.c b/servers/slapd/back-ldap/modify.c index fb7bfc6699..269ebf4591 100644 --- a/servers/slapd/back-ldap/modify.c +++ b/servers/slapd/back-ldap/modify.c @@ -61,45 +61,89 @@ ldap_back_modify( LDAPMod *mods; Modifications *ml; int i; - char *mdn, *mapped; + char *mdn = NULL, *mapped; lc = ldap_back_getconn(li, conn, op); if ( !lc || !ldap_back_dobind( lc, op ) ) { return( -1 ); } - mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); - if ( mdn == NULL ) { + /* + * Rewrite the modify dn, if needed + */ +#ifdef ENABLE_REWRITE + switch ( rewrite_session( li->rwinfo, "modifyDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> modifyDN: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: return( -1 ); } +#else /* !ENABLE_REWRITE */ + mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); +#endif /* !ENABLE_REWRITE */ for (i=0, ml=modlist; ml; i++,ml=ml->sml_next) ; mods = (LDAPMod *)ch_malloc(i*sizeof(LDAPMod)); - if (mods == NULL) - return( -1 ); + if (mods == NULL) { + goto cleanup; + } modv = (LDAPMod **)ch_malloc((i+1)*sizeof(LDAPMod *)); if (modv == NULL) { - free(mods); - return( -1 ); + goto cleanup; } for (i=0, ml=modlist; ml; ml=ml->sml_next) { mapped = ldap_back_map(&li->at_map, ml->sml_desc->ad_cname->bv_val, 0); - if (mapped != NULL) { - modv[i] = &mods[i]; - mods[i].mod_op = ml->sml_op | LDAP_MOD_BVALUES; - mods[i].mod_type = mapped; - mods[i].mod_bvalues = ml->sml_bvalues; - i++; + if (mapped == NULL) { + continue; } + + modv[i] = &mods[i]; + mods[i].mod_op = ml->sml_op | LDAP_MOD_BVALUES; + mods[i].mod_type = mapped; + +#ifdef ENABLE_REWRITE + /* + * FIXME: dn-valued attrs should be rewritten + * to allow their use in ACLs at the back-ldap + * level. + */ + if ( strcmp( ml->sml_desc->ad_type->sat_syntax->ssyn_oid, + SLAPD_DN_SYNTAX ) == 0 ) { + ldap_dnattr_rewrite( li->rwinfo, + ml->sml_bvalues, conn ); + } +#endif /* ENABLE_REWRITE */ + + mods[i].mod_bvalues = ml->sml_bvalues; + i++; } modv[i] = 0; ldap_modify_s( lc->ld, mdn, modv ); - free( mdn ); + +cleanup: +#ifdef ENABLE_REWRITE + if ( mdn != dn ) { +#endif /* ENABLE_REWRITE */ + free( mdn ); +#ifdef ENABLE_REWRITE + } +#endif /* ENABLE_REWRITE */ free(mods); free(modv); return( ldap_back_op_result( lc, op )); } + diff --git a/servers/slapd/back-ldap/modrdn.c b/servers/slapd/back-ldap/modrdn.c index 66761c0a73..74c2922ce8 100644 --- a/servers/slapd/back-ldap/modrdn.c +++ b/servers/slapd/back-ldap/modrdn.c @@ -60,10 +60,10 @@ ldap_back_modrdn( struct ldapinfo *li = (struct ldapinfo *) be->be_private; struct ldapconn *lc; - char *mdn, *mnewSuperior; + char *mdn = NULL, *mnewSuperior = NULL; lc = ldap_back_getconn( li, conn, op ); - if ( !lc ) { + if ( !lc || !ldap_back_dobind(lc, op) ) { return( -1 ); } @@ -71,26 +71,78 @@ ldap_back_modrdn( int version = LDAP_VERSION3; ldap_set_option( lc->ld, LDAP_OPT_PROTOCOL_VERSION, &version); + /* + * Rewrite the new superior, if defined and required + */ +#ifdef ENABLE_REWRITE + switch ( rewrite_session( li->rwinfo, "newSuperiorDn", + newSuperior, conn, &mnewSuperior ) ) { + case REWRITE_REGEXEC_OK: + if ( mnewSuperior == NULL ) { + mnewSuperior = ( char * )newSuperior; + } + Debug( LDAP_DEBUG_ARGS, "rw> newSuperiorDn:" + " \"%s\" -> \"%s\"\n%s", + newSuperior, mnewSuperior, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return( -1 ); + } +#else /* !ENABLE_REWRITE */ mnewSuperior = ldap_back_dn_massage( li, - ch_strdup( newSuperior ), 0 ); + ch_strdup( newSuperior ), 0 ); if ( mnewSuperior == NULL ) { return( -1 ); } +#endif /* !ENABLE_REWRITE */ } - if ( !ldap_back_dobind(lc, op) ) { - return( -1 ); - } +#ifdef ENABLE_REWRITE + /* + * Rewrite the modrdn dn, if required + */ + switch ( rewrite_session( li->rwinfo, "modrDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> modrDn: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); - mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); - if ( mdn == NULL ) { + case REWRITE_REGEXEC_ERR: return( -1 ); } +#else /* !ENABLE_REWRITE */ + mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 ); +#endif /* !ENABLE_REWRITE */ ldap_rename2_s( lc->ld, mdn, newrdn, mnewSuperior, deleteoldrdn ); +#ifdef ENABLE_REWRITE + if ( mdn != dn ) { +#endif /* ENABLE_REWRITE */ free( mdn ); - if ( mnewSuperior ) free( mnewSuperior ); +#ifdef ENABLE_REWRITE + } +#endif /* ENABLE_REWRITE */ + if ( mnewSuperior != NULL +#ifdef ENABLE_REWRITE + && mnewSuperior != newSuperior +#endif /* ENABLE_REWRITE */ + ) { + free( mnewSuperior ); + } return( ldap_back_op_result( lc, op ) ); } diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c index b65bb3e8d9..1b0e3a20ee 100644 --- a/servers/slapd/back-ldap/search.c +++ b/servers/slapd/back-ldap/search.c @@ -70,9 +70,12 @@ ldap_back_search( struct ldapconn *lc; struct timeval tv; LDAPMessage *res, *e; - int count, rc, msgid, sres = LDAP_SUCCESS; + int count, rc = 0, msgid, sres = LDAP_SUCCESS; char *match = NULL, *err = NULL; - char *mbase, *mapped_filter, **mapped_attrs; + char *mbase = NULL, *mapped_filter = NULL, **mapped_attrs = NULL; +#ifdef ENABLE_REWRITE + char *mfilter = NULL, *mmatch = NULL; +#endif /* ENABLE_REWRITE */ lc = ldap_back_getconn(li, conn, op); if ( !lc ) { @@ -90,17 +93,73 @@ ldap_back_search( return( -1 ); } - mbase = ldap_back_dn_massage( li, ch_strdup( base ), 0 ); - if ( mbase == NULL ) { - return -1; + /* + * Rewrite the search base, if required + */ +#ifdef ENABLE_REWRITE + switch ( rewrite_session( li->rwinfo, "searchBase", + base, conn, &mbase ) ) { + case REWRITE_REGEXEC_OK: + if ( mbase == NULL ) { + mbase = ( char * )base; + } + Debug( LDAP_DEBUG_ARGS, "rw> searchBase: \"%s\" -> \"%s\"\n%s", + base, mbase, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + rc = -1; + goto finish; } + + /* + * Rewrite the search filter, if required + */ + switch ( rewrite_session( li->rwinfo, "searchFilter", + filterstr, conn, &mfilter ) ) { + case REWRITE_REGEXEC_OK: + if ( mfilter == NULL || mfilter[0] == '\0') { + if ( mfilter != NULL ) { + free( mfilter ); + } + mfilter = ( char * )filterstr; + } + Debug( LDAP_DEBUG_ARGS, + "rw> searchFilter: \"%s\" -> \"%s\"\n%s", + filterstr, mfilter, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + case REWRITE_REGEXEC_ERR: + rc = -1; + goto finish; + } +#else /* !ENABLE_REWRITE */ + mbase = ldap_back_dn_massage( li, ch_strdup( base ), 0 ); +#endif /* !ENABLE_REWRITE */ - mapped_filter = ldap_back_map_filter(li, (char *)filterstr, 0); + mapped_filter = ldap_back_map_filter(&li->at_map, &li->oc_map, +#ifdef ENABLE_REWRITE + (char *)mfilter, +#else /* !ENABLE_REWRITE */ + (char *)filterstr, +#endif /* !ENABLE_REWRITE */ + 0); if ( mapped_filter == NULL ) { +#ifdef ENABLE_REWRITE + mapped_filter = (char *)mfilter; +#else /* !ENABLE_REWRITE */ mapped_filter = (char *)filterstr; +#endif /* !ENABLE_REWRITE */ } - mapped_attrs = ldap_back_map_attrs(li, attrs, 0); + mapped_attrs = ldap_back_map_attrs(&li->at_map, attrs, 0); if ( mapped_attrs == NULL ) { mapped_attrs = attrs; } @@ -109,16 +168,8 @@ ldap_back_search( attrsonly)) == -1) { fail: - if (match) - free(match); - if (err) - free(err); - if (mapped_attrs != attrs) - charray_free(mapped_attrs); - if (mapped_filter != filterstr) - free(mapped_filter); - free(mbase); - return( ldap_back_op_result(lc, op) ); + rc = ldap_back_op_result(lc, op); + goto finish; } /* We pull apart the ber result, stuff it into a slapd entry, and @@ -139,6 +190,7 @@ fail: if (ab) { ldap_abandon(lc->ld, msgid); + rc = 0; goto finish; } if (rc == 0) { @@ -163,20 +215,78 @@ fail: if (rc == -1) goto fail; +#ifdef ENABLE_REWRITE + /* + * Rewrite the matched portion of the search base, if required + */ + if ( match != NULL ) { + switch ( rewrite_session( li->rwinfo, "matchedDn", + match, conn, &mmatch ) ) { + case REWRITE_REGEXEC_OK: + if ( mmatch == NULL ) { + mmatch = ( char * )match; + } + Debug( LDAP_DEBUG_ARGS, "rw> matchedDn:" + " \"%s\" -> \"%s\"\n%s", + match, mmatch, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + rc = -1; + goto finish; + } + } + + send_search_result( conn, op, sres, + mmatch, err, NULL, NULL, count ); + +#else /* !ENABLE_REWRITE */ send_search_result( conn, op, sres, match, err, NULL, NULL, count ); +#endif /* !ENABLE_REWRITE */ finish: - if (match) + if ( match ) { +#ifdef ENABLE_REWRITE + if ( mmatch != match ) { + free( mmatch ); + } +#endif /* ENABLE_REWRITE */ free(match); - if (err) - free(err); - if (mapped_attrs != attrs) - charray_free(mapped_attrs); - if (mapped_filter != filterstr) - free(mapped_filter); - free(mbase); - return( 0 ); + } + if ( err ) { + free( err ); + } + if ( mapped_attrs != attrs ) { + charray_free( mapped_attrs ); + } +#ifdef ENABLE_REWRITE + if ( mapped_filter != mfilter ) { + free( mapped_filter ); + } + if ( mfilter != filterstr ) { + free( mfilter ); + } +#else /* !ENABLE_REWRITE */ + if ( mapped_filter != filterstr ) { + free( mapped_filter ); + } +#endif /* !ENABLE_REWRITE */ + +#ifdef ENABLE_REWRITE + if ( mbase != base ) { +#endif /* ENABLE_REWRITE */ + free( mbase ); +#ifdef ENABLE_REWRITE + } +#endif /* ENABLE_REWRITE */ + + return rc; } static void @@ -198,7 +308,39 @@ ldap_send_entry( struct berval *bv; const char *text; +#ifdef ENABLE_REWRITE + char *dn; + + dn = ldap_get_dn(lc->ld, e); + if ( dn == NULL ) { + return; + } + + /* + * Rewrite the dn of the result, if needed + */ + switch ( rewrite_session( li->rwinfo, "searchResult", + dn, lc->conn, &ent.e_dn ) ) { + case REWRITE_REGEXEC_OK: + if ( ent.e_dn == NULL ) { + ent.e_dn = dn; + } else { + Debug( LDAP_DEBUG_ARGS, "rw> searchResult: \"%s\"" + " -> \"%s\"\n%s", dn, ent.e_dn, "" ); + free( dn ); + dn = NULL; + } + break; + + case REWRITE_REGEXEC_ERR: + case REWRITE_REGEXEC_UNWILLING: + free( dn ); + return; + } +#else /* !ENABLE_REWRITE */ ent.e_dn = ldap_back_dn_restore( li, ldap_get_dn(lc->ld, e), 0 ); +#endif /* !ENABLE_REWRITE */ + ent.e_ndn = ch_strdup( ent.e_dn ); (void) dn_normalize( ent.e_ndn ); ent.e_id = 0; @@ -228,7 +370,7 @@ ldap_send_entry( } else if ( strcasecmp( mapped, "objectclass" ) == 0 ) { int i, last; for ( last = 0; attr->a_vals[last]; last++ ) ; - for ( i = 0; bv = attr->a_vals[i]; i++ ) { + for ( i = 0; ( bv = attr->a_vals[i] ); i++ ) { mapped = ldap_back_map(&li->oc_map, bv->bv_val, 1); if (mapped == NULL) { ber_bvfree(attr->a_vals[i]); @@ -244,7 +386,59 @@ ldap_send_entry( bv->bv_len = strlen( mapped ); } } + +#ifdef ENABLE_REWRITE + /* + * It is necessary to try to rewrite attributes with + * dn syntax because they might be used in ACLs as + * members of groups; since ACLs are applied to the + * rewritten stuff, no dn-based subecj clause could + * be used at the ldap backend side (see + * http://www.OpenLDAP.org/faq/data/cache/452.html) + * The problem can be overcome by moving the dn-based + * ACLs to the target directory server, and letting + * everything pass thru the ldap backend. + */ + } else if ( strcmp( attr->a_desc->ad_type->sat_syntax->ssyn_oid, + SLAPD_DN_SYNTAX ) == 0 ) { + int i; + for ( i = 0; ( bv = attr->a_vals[ i ] ); i++ ) { + char *newval; + + switch ( rewrite_session( li->rwinfo, + "searchResult", + bv->bv_val, + lc->conn, &newval )) { + case REWRITE_REGEXEC_OK: + /* left as is */ + if ( newval == NULL ) { + break; + } + Debug( LDAP_DEBUG_ARGS, + "rw> searchResult on attr=%s: \"%s\" -> \"%s\"\n", + attr->a_desc->ad_type->sat_cname, + bv->bv_val, newval ); + + free( bv->bv_val ); + bv->bv_val = newval; + bv->bv_len = strlen( newval ); + + break; + + case REWRITE_REGEXEC_UNWILLING: + + case REWRITE_REGEXEC_ERR: + /* + * FIXME: better give up, + * skip the attribute + * or leave it untouched? + */ + break; + } + } +#endif /* ENABLE_REWRITE */ } + *attrp = attr; attrp = &attr->a_next; } @@ -265,3 +459,4 @@ ldap_send_entry( if ( ent.e_ndn ) free( ent.e_ndn ); } + diff --git a/servers/slapd/back-ldap/suffixmassage.c b/servers/slapd/back-ldap/suffixmassage.c index aa49ef21c4..de771055e4 100644 --- a/servers/slapd/back-ldap/suffixmassage.c +++ b/servers/slapd/back-ldap/suffixmassage.c @@ -30,6 +30,8 @@ #include "portable.h" +#ifndef ENABLE_REWRITE + #include #include @@ -154,4 +156,5 @@ ldap_back_dn_restore( return dn; } +#endif /* !ENABLE_REWRITE */ diff --git a/servers/slapd/back-ldap/unbind.c b/servers/slapd/back-ldap/unbind.c index 574ef6ba60..49b2f3ef19 100644 --- a/servers/slapd/back-ldap/unbind.c +++ b/servers/slapd/back-ldap/unbind.c @@ -62,14 +62,21 @@ ldap_back_conn_destroy( lc_curr.conn = conn; ldap_pvt_thread_mutex_lock( &li->conn_mutex ); - lc = avl_delete( &li->conntree, (caddr_t)&lc_curr, conn_cmp ); + lc = avl_delete( &li->conntree, (caddr_t)&lc_curr, ldap_back_conn_cmp ); ldap_pvt_thread_mutex_unlock( &li->conn_mutex ); if (lc) { Debug( LDAP_DEBUG_TRACE, "=>ldap_back_conn_destroy: destroying conn %d\n", lc->conn->c_connid, 0, 0 ); - + +#ifdef ENABLE_REWRITE + /* + * Cleanup rewrite session + */ + rewrite_session_delete( li->rwinfo, conn ); +#endif /* ENABLE_REWRITE */ + /* * Needs a test because the handler may be corrupted, * and calling ldap_unbind on a corrupted header results diff --git a/servers/slapd/back-meta/Changes b/servers/slapd/back-meta/Changes new file mode 100644 index 0000000000..a032bd43ba --- /dev/null +++ b/servers/slapd/back-meta/Changes @@ -0,0 +1,68 @@ +Copyright 2001, Pierangelo Masarati, All rights reserved. + +* Mon Apr 30 2001 Pierangelo Masarati +- split back-meta from back-ldap +- inplement init, config, bind, search, unbind + +* Tue May 01 2001 Pierangelo Masarati +- refined search propagation based on scope; in case of base shorter + than candidate target suffix: + "sub" propagates to all candidate targets, + "one" propagates to candidate targets only if base is + no more than one level shorter than suffix; in such + case scope is changed into "base" + "base" does not propagate (no such object error) + +* Tue May 01 2001 Pierangelo Masarati +- added checks in config.c +- fixed leaks in search.c + +* Fri May 04 2001 Pierangelo Masarati +- added dn cache +- added test + +* Sat May 05 2001 Pierangelo Masarati +- added missing functions +- fixed flaw in group/attribute funcs (affects also back-ldap) + now meta_back_group works fine (group ACLS); nothing to say + about meta_back_attribute: unable to trigger it! +- attributes with dn syntax are rewritten back in search results + (otherwise "dnattr"-style ACLs don't work). Now attrs with + dn syntax need be rewritten forth in add/modify ... + +* Sun May 06 2001 Pierangelo Masarati +- attributes with dn syntax are rewritten during add/modify operations + (otherwise "dnattr"-style ACLs don't work). +- same back/forth rewriting has been applied to back-ldap; + actually, the add/modify rewriting function has been implemented + in bacl-ldap (ldap_dnattr_rewrite) and recycled in back-meta +- fixed bug in invocation of meta_back_dobind in delete.c +- code for deletion of "lastmod" attrs has been commented; + explicit "lastmod off" has been recommended in FAQ. +- fixed a missing return in suffix_massage_config + +* Sun May 06 2001 Pierangelo Masarati +- fixed flaw in group.c both in back-ldap and back-meta: the group ndn + was not rewritten. + +* Tue May 08 2001 Pierangelo Masarati +- removed the limitation on target naming contexts; now they can be the same +- added support for default target; it may be disabled or set to one of + the targets, resulting in that target being selected any time there's + a non-unique target selection (the target is missing or multiple targets + are selected for a add/modify/delete/compare/... operation). + TODO: should be more flexible, e.g. let the administrator decide + whether the default target may be used or not. +- fixed a subtle bind/cache bug. +- added helpers to clear target connections when they're no longer candidate. + +* Wed May 09 2001 Pierangelo Masarati +- reworked connection stuff in a separate file +- reworked meta_back_getconn to handle singe/multiple target selection + correctly and atomicly +- checked attribute mapping stuff + +* Fri May 11 2001 Pierangelo Masarati +- reworked compare to spawn the request on candidate targets (need to check + at most one matches!) + diff --git a/servers/slapd/back-meta/Copyright b/servers/slapd/back-meta/Copyright new file mode 100644 index 0000000000..0e0faa476e --- /dev/null +++ b/servers/slapd/back-meta/Copyright @@ -0,0 +1,63 @@ +Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. +COPYING RESTRICTIONS APPLY, see COPYRIGHT file + +Copyright 2001, Pierangelo Masarati, All rights reserved. + +This work has been developed to fulfill the requirements +of SysNet s.n.c. and it has been donated +to the OpenLDAP Foundation in the hope that it may be useful +to the Open Source community, but WITHOUT ANY WARRANTY. + +Permission is granted to anyone to use this software for any purpose +on any computer system, and to alter it and redistribute it, subject +to the following restrictions: + +1. The author and SysNet s.n.c. are not responsible for the consequences + of use of this software, no matter how awful, even if they arise from + flaws in it. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. Since few users ever read sources, + credits should appear in the documentation. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. Since few users + ever read sources, credits should appear in the documentation. + SysNet s.n.c. cannot be responsible for the consequences of the + alterations. + +4. This notice may not be removed or altered. + + +This software is based on the backend back-ldap, implemented +by Howard Chu , and modified by Mark Valence +, Pierangelo Masarati and other +contributors. The contribution of the original software to the present +implementation is acknowledged in this copyright statement. + +A special acknowledgement goes to Howard for the overall architecture +(and for borrowing large pieces of code), and to Mark, who implemented +from scratch the attribute/objectclass mapping. + +The original copyright statement follows. + +Copyright 1999, Howard Chu, All rights reserved. + +Permission is granted to anyone to use this software for any purpose +on any computer system, and to alter it and redistribute it, subject +to the following restrictions: + +1. The author is not responsible for the consequences of use of this + software, no matter how awful, even if they arise from flaws in it. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. Since few users ever read sources, + credits should appear in the documentation. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. Since few users + ever read sources, credits should appear in the documentation. + +4. This notice may not be removed or altered. + + diff --git a/servers/slapd/back-meta/Documentation b/servers/slapd/back-meta/Documentation new file mode 100644 index 0000000000..a8e570ad56 --- /dev/null +++ b/servers/slapd/back-meta/Documentation @@ -0,0 +1,285 @@ +Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. +COPYING RESTRICTIONS APPLY, see COPYRIGHT file + +Copyright 2001, Pierangelo Masarati, All rights reserved. + + + + Metadirectory backend. + +This is a brief introduction to the core functionalities of back-meta. + + + + - Introduction. + +Back-meta implements a backend for OpenLDAP's slapd. It performs basic +LDAP proxying with respect to a set of remote LDAP servers, called +"targets". The information contained in these servers can be presented +as belonging to a single Directory Information Tree (DIT). + +A basic knowledge of the functionality of back-ldap is recommended. +This backend has been designed as an enhancement of back-ldap. +The two backends share many features (actually they also share portions +of code). While back-ldap is intended to proxy operations directed +to a single server, back-meta is mainly intended for proxying +of multiple servers and possibly naming context masquerading. +These features, although useful in many scenarios, may result in +excessive overhead for some applications, so its use should be +carefully considered. + +In the examples section, some typical scenarios will be discussed. + + + + - Common configuration directives + +The backend uses most of the common configuration directives. Its +configuration block starts with the "database" directive: + + database meta + +At least a "suffix" directive is required. + +Note: as with back-ldap, operational attributes related to entry +creation/modification should not be used, as they would be passed +to the target servers, generating an error. Moreover, it makes +little sense to use such attributes in proxying, as the proxy +server doesn't actually store data, so it should have no knowledge +of such attributes. While code to strip the modification attributes +has been put in place (and #ifdef's), it implies unmotivated overhead. +So it is strongly recommended to set + + lastmod off + +for every back-ldap/back-meta backend. + + + + - Special configuration directives + +Target configuration starts with the "uri" directive. All the +configuration directives that are not specific to targets should +be defined first for clarity, including those that are common to +all backends. They are: + + default-target none + +This directive forces the backend to reject all those operations +that must resolve to a single target in case none or multiple +targets are selected. They include: add, delete, modify, modrdn; +compare is also included, although a different behavior might be +considered in the future. This directive can also be used when +processing targets to mark a specific target as default. + + dncache-ttl {forever|disabled|} + +This directive sets the time-to-live of the dn cache. This caches +the target that holds a given dn to speed up target selection +in case multiple targets would result from an uncached search; +forever means cache never expires; disabled means no dn caching; +otherwise a valid ( > 0 ) ttl in seconds is required. + + + + - Target specification + +Target specification starts with a "uri" directive: + + uri ://[[:]]/ + +The "server" directive that was allowed in back-ldap has been discarded +in back-meta. The part can be anything ldap_initialize(3) +accepts ({ldap|ldaps|ldapi} and variants); and may be +omitted, defaulting to whatever is set in /etc/ldap.conf (correct me!?!). +The part is mandatory. It must end with one of the +naming contexts defined for the backend, e.g.: + + suffix "dc=foo,dc=com" + uri "ldap://x.foo.com/dc=x,dc=foo,dc=com" + +The part doesn't need to be unique across the targets; +it may also match one of the values of the "suffix" directive. + + default-target [] + +the "default-target" directive can also be used during target +specification. With no arguments it marks the current target as +the default. The optional number marks target as the +default one, starting from 1. Target must be defined. + + binddn + +This directive, as in back-ldap, allows to define the dn that is +used to query the target server for acl checking; it should have +read access on the target server to attributes used on the proxy +for acl checking. There is no risk of giving away such values; +they are only used to check permissions. + + bindpw + +This directive sets the password for acl checking in conjunction +with the above mentioned "binddn" directive. + + rewrite* ... + + suffixmassage <virtual naming context> <real naming context> + +All the directives starting with "rewrite" refer to the rewrite engine +that has been added to slapd. The "suffixmassage" directive was +introduced in back-ldap to allow suffix massaging while proxying. +It has been obsoleted by the rewriting tools. However, both for +backward compatibility and for ease of configuration when simple +suffix massage is required, it has been preserved. It wraps the +basic rewriting instruction that perform suffix massaging. + +Note: this also fixes a flaw in suffix massaging, which operated +on (case insensitive) DNs instead of normalized DNs, +so "dc=foo, dc=com" would not match "dc=foo,dc=com". + +See the "rewrite" section. + + map {objectClass|attribute} {<source>|*} [<dest>|*] + +objectClass/attribute mapping stuff. This has been inherited from +the work made by Mark Valence on the back-ldap backend. +See the mapping section. + + + + - Scenarios + +A powerful (and in some sense dangerous) rewrite engine has been added +to both back-ldap and back-meta. While the former can gain limited +beneficial effects from rewriting stuff, the latter can become +an amazingly powerful tool. + +Consider a couple of scenarios first. + +1) Two directory servers share two levels of naming context; +say "dc=a,dc=foo,dc=com" and "dc=b,dc=foo,dc=com". Then, an +unambiguous back-meta can be configured as: + + database meta + suffix "dc=foo,dc=com" + + uri "ldap://a.foo.com/dc=a,dc=foo,dc=com" + + uri "ldap://b.foo.com/dc=b,dc=foo,dc=com" + +Operations directed to a specific target can be easily resolved +because there are no ambiguities. The only operation that may +resolve to multiple targets is a search with base "dc=foo,dc=com" +and scope at least "one", which results in spawning two searches +to the targets. + +2a) Two directory servers don't share any portion of naming context, +but they'd present as a single DIT. [Caveat: uniqueness of +(massaged) entries among the two servers is assumed; integrity +checks risk to incurr in excessive overhead.] +Say we have "dc=bar,dc=org" and "o=Foo,c=US", and we'd like them to +present as branches of "dc=foo,dc=com", say "dc=a,dc=foo,dc=com" +and "dc=b,dc=foo,dc=com". Then we need to configure our back-meta as: + + database meta + suffix "dc=foo,dc=com" + + uri "ldap://a.bar.com/dc=a,dc=foo,dc=com" + suffixmassage "dc=a,dc=foo,dc=com" "dc=bar,dc=org" + + uri "ldap://b.foo.com/dc=b,dc=foo,dc=com" + suffixmassage "dc=b,dc=foo,dc=com" "o=Foo,c=US" + +Again, operations can be resolved without ambiguity, although +some rewriting is required. Notice that the virtual naming context +of each target is a branch of the database's naming context; it +is rewritten back and forth when operations are performed towards +the target servers. What "back and forth" means will be clarified +later. + +When a search with base "dc=foo,dc=com" is attempted, if the +scope is "base" it fails with "no such object"; in fact, the +common root of the two targets (prior to massaging) does not +exists. If the scope is "one", both targets are contacted with +the base replaced by each target's base; the scope is decreased +to "base". In general, the scope "one" search is honored, +and the scope is decreased, only when the incoming base is +at most one level lower of a target's naming context (prior +to massaging). +Finally, if the scope is "sub" the incoming base is replaced +by each target's unmassaged naming context, and the scope +is not altered. + +2b) Consider the above reported scenario with the two servers +sharing the same naming context: + + database meta + suffix "dc=foo,dc=com" + + uri "ldap://a.bar.com/dc=foo,dc=com" + suffixmassage "dc=foo,dc=com" "dc=bar,dc=org" + + uri "ldap://b.foo.com/dc=foo,dc=com" + suffixmassage "dc=foo,dc=com" "o=Foo,c=US" + +All the previous considerations hold, except that now there is +no way to unambiguously resolve a dn. In this case, all the +operations that require an unambiguous target selection will +fail unless the dn is already cached or a default target has +been set. + + + + - Rewriting + +This part of the document is being prepared. At present you may consult +the RATIONALE in libraries/librewrite. + + + + - Objectclass/attribute mapping + +This part of the document is being prepared. At present you may stick with + +http://www.openldap.org/lists/openldap-devel/200102/msg00006.html + + + + - ACL +Note on ACLs: at present you may add whatever ACL rule you desire +to back-meta (as well as to back-ldap). However, the meaning of an ACL +on a proxy may require some considerations. Two philosophies may be +considered: + +a) the remote server dictates the permissions; the proxy simply passes +back what it gets from the remore server. + +b) the remote server unveils "everything"; the proxy is responsible +for protecting data from unauthorized access. + +Of course the latter sounds unreasonable, but it is not. It is possible +to imagine scenarios in which a remote host discloses data that can +be considered "public" in an intranet, and a proxy that connects it to +the internet may impose additional constraints. To this purpose, the +proxy should be able to comply with all the ACL matching criteria that +the server supports. This has been achieved with regard to all the +criteria supported by slapd except a secial subtle case (please notify +me if you can find other exceptions). +The rule + +access to dn="<dn>" attr=<attr> + by dnattr=<dnattr> read + by * none + +cannot be matched IFF: +- the operation dn (the one that bound) is "<dn>", and +- the entry whose <attr> is being accessed is again <dn>, and +- the attribute that determines membership, <dnattr>, has not + been required (e.g. in a search) + +In fact this ACL is resolved by slapd considering the entry it retrieved +from the remote server without requiring any further intervention of the +backend, so, if the <dnattr> attribute has not been fetched, the match +cannot be accomplished because the attribute is not present, not because +no value matches the requirement. + diff --git a/servers/slapd/back-meta/Makefile.in b/servers/slapd/back-meta/Makefile.in new file mode 100644 index 0000000000..2b796c3224 --- /dev/null +++ b/servers/slapd/back-meta/Makefile.in @@ -0,0 +1,26 @@ +# $OpenLDAP$ + +SRCS = init.c config.c search.c bind.c unbind.c add.c compare.c \ + delete.c modify.c modrdn.c group.c attribute.c \ + conn.c candidates.c dncache.c +OBJS = init.lo config.lo search.lo bind.lo unbind.lo add.lo compare.lo \ + delete.lo modify.lo modrdn.lo group.lo attribute.lo \ + conn.lo candidates.lo dncache.lo + +LDAP_INCDIR= ../../../include +LDAP_LIBDIR= ../../../libraries + +BUILD_OPT = "--enable-meta" +BUILD_MOD = @BUILD_META@ +LINKAGE = @BUILD_META_DYNAMIC@ + +LIBBASE = back_meta + +XINCPATH = -I.. -I$(srcdir)/.. +XDEFS = $(MODULES_CPPFLAGS) + +all-local-lib: ../.backend + +../.backend: lib$(LIBBASE).a + @touch $@ + diff --git a/servers/slapd/back-meta/TODO b/servers/slapd/back-meta/TODO new file mode 100644 index 0000000000..11fa8cc4ae --- /dev/null +++ b/servers/slapd/back-meta/TODO @@ -0,0 +1,20 @@ +* Short term: + +- add missing functions (FIXED) + +- review per-target error handling + +- dn cache and cache exploitation to refine the candidate selection (?) (FIXED) + +- review the group and attribute stuff (also in back-ldap!) (FIXED, need to + test attribute) + +- rework compare and bind to attempt to operate on all candidate entries + while checking at most one succeedes + +- clear previously bound targets when the bind is repeated + +* Long term: + +- distributed entries + diff --git a/servers/slapd/back-meta/add.c b/servers/slapd/back-meta/add.c new file mode 100644 index 0000000000..4f50305de0 --- /dev/null +++ b/servers/slapd/back-meta/add.c @@ -0,0 +1,189 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + + +#include "portable.h" + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +int +meta_back_add( + Backend *be, + Connection *conn, + Operation *op, + Entry *e +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + struct metaconn *lc; + int i, candidate = -1; + Attribute *a; + LDAPMod **attrs; + char *mdn = NULL, *mapped; + + /* + * get the current connection + */ + lc = meta_back_getconn( li, conn, op, META_OP_REQUIRE_SINGLE, + e->e_ndn, &candidate ); + if ( !lc || !meta_back_dobind( lc, op ) ) { + return -1; + } + + /* + * Rewrite the add dn, if needed + */ + switch ( rewrite_session( li->targets[ candidate ]->rwinfo, + "addDn", e->e_dn, conn, &mdn )) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = e->e_dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> addDn: \"%s\" -> \"%s\"\n%s", + e->e_dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return -1; + } + + /* Count number of attributes in entry */ + for ( i = 1, a = e->e_attrs; a; i++, a = a->a_next ); + + /* Create array of LDAPMods for ldap_add() */ + attrs = ch_malloc( sizeof( LDAPMod * )*i ); + + for ( i = 0, a = e->e_attrs; a; a = a->a_next ) { + /* + * lastmod should always be <off>, so that + * creation/modification operational attrs + * of the target directory are used, if available + */ +#if 0 + if ( !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_creatorsName->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_createTimestamp->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_modifiersName->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_modifyTimestamp->ad_cname->bv_val ) + ) { + continue; + } +#endif + + mapped = ldap_back_map( &li->targets[ candidate ]->at_map, + a->a_desc->ad_cname->bv_val, 0); + if ( mapped == NULL ) { + continue; + } + + attrs[ i ] = ch_malloc( sizeof( LDAPMod ) ); + if ( attrs[ i ] == NULL ) { + continue; + } + attrs[ i ]->mod_op = LDAP_MOD_BVALUES; + attrs[ i ]->mod_type = mapped; + + /* + * FIXME: dn-valued attrs should be rewritten + * to allow their use in ACLs at the back-ldap + * level. + */ + if ( strcmp( a->a_desc->ad_type->sat_syntax->ssyn_oid, + SLAPD_DN_SYNTAX ) == 0 ) { + ldap_dnattr_rewrite( li->targets[ candidate ]->rwinfo, + a->a_vals, conn ); + } + + attrs[ i ]->mod_vals.modv_bvals = a->a_vals; + i++; + } + attrs[ i ] = NULL; + + ldap_add_s( lc->conns[ candidate ]->ld, mdn, attrs ); + for ( --i; i >= 0; --i ) { + free( attrs[ i ] ); + } + free( attrs ); + if ( mdn != e->e_dn ) { + free( mdn ); + } + return meta_back_op_result( lc, op ); +} + diff --git a/servers/slapd/back-meta/attribute.c b/servers/slapd/back-meta/attribute.c new file mode 100644 index 0000000000..46c4623352 --- /dev/null +++ b/servers/slapd/back-meta/attribute.c @@ -0,0 +1,208 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + + +/* return 0 IFF we can retrieve the attributes + * of entry with e_ndn + */ + +/* + * FIXME: I never testd this function; I know it compiles ... :) + */ +int +meta_back_attribute( + Backend *be, + Connection *conn, + Operation *op, + Entry *target, + const char *e_ndn, + AttributeDescription *entry_at, + struct berval ***vals +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + int rc = 1, i, j, count, is_oc, candidate; + Attribute *attr; + struct berval **abv, **v; + char **vs, *mapped; + LDAPMessage *result, *e; + char *gattr[ 2 ]; + LDAP *ld; + char *me_ndn; + + *vals = NULL; + if ( target != NULL && strcmp( target->e_ndn, e_ndn ) == 0 ) { + /* we already have a copy of the entry */ + /* attribute and objectclass mapping has already been done */ + attr = attr_find( target->e_attrs, entry_at ); + if ( attr == NULL ) { + return 1; + } + + for ( count = 0; attr->a_vals[ count ] != NULL; count++ ) + ; + v = ch_calloc( ( count + 1 ), sizeof( struct berval * ) ); + if ( v != NULL ) { + for ( j = 0, abv = attr->a_vals; --count >= 0; abv++ ) { + if ( ( *abv )->bv_len > 0 ) { + v[ j ] = ber_bvdup( *abv ); + if ( v[ j ] == NULL ) { + break; + } + } + } + v[ j ] = NULL; + *vals = v; + rc = 0; + } + + return rc; + } /* else */ + + candidate = meta_back_select_unique_candidate( li, e_ndn ); + if ( candidate == -1 ) { + return 1; + } + + mapped = ldap_back_map( &li->targets[ candidate ]->at_map, + entry_at->ad_cname->bv_val, 0 ); + if ( mapped == NULL ) + return 1; + + rc = ldap_initialize( &ld, li->targets[ candidate ]->uri ); + if ( rc != LDAP_SUCCESS ) { + return 1; + } + + rc = ldap_bind_s( ld, li->targets[ candidate ]->binddn, + li->targets[ candidate ]->bindpw, LDAP_AUTH_SIMPLE ); + if ( rc != LDAP_SUCCESS) { + return 1; + } + + gattr[ 0 ] = mapped; + gattr[ 1 ] = NULL; + if ( ldap_search_ext_s( ld, e_ndn, LDAP_SCOPE_BASE, "(objectclass=*)", + gattr, 0, NULL, NULL, LDAP_NO_LIMIT, + LDAP_NO_LIMIT, &result) == LDAP_SUCCESS) { + if ( ( e = ldap_first_entry( ld, result ) ) != NULL ) { + vs = ldap_get_values( ld, e, mapped ); + if ( vs != NULL ) { + for ( count = 0; vs[ count ] != NULL; + count++ ) { } + v = ch_calloc( ( count + 1 ), + sizeof( struct berval * ) ); + if ( v == NULL ) { + ldap_value_free( vs ); + } else { + is_oc = ( strcasecmp( "objectclass", mapped ) == 0 ); + for ( i = 0, j = 0; i < count; i++ ) { + if ( !is_oc ) { + v[ j ] = ber_bvstr( vs[ i ] ); + if ( v[ j ] == NULL ) { + ch_free( vs[ i ] ); + } else { + j++; + } + } else { + mapped = ldap_back_map( &li->targets[ candidate ]->oc_map, vs[ i ], 1 ); + if ( mapped ) { + mapped = ch_strdup( mapped ); + if ( mapped ) { + v[ j ] = ber_bvstr( mapped ); + if ( v[ j ] ) { + j++; + } + } + } + ch_free( vs[ i ] ); + } + } + v[ j ] = NULL; + *vals = v; + rc = 0; + ch_free( vs ); + } + } + } + ldap_msgfree( result ); + } + ldap_unbind(ld); + + return(rc); +} + diff --git a/servers/slapd/back-meta/back-meta.h b/servers/slapd/back-meta/back-meta.h new file mode 100644 index 0000000000..5d0c433ce3 --- /dev/null +++ b/servers/slapd/back-meta/back-meta.h @@ -0,0 +1,284 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#ifndef SLAPD_LDAP_H +#error "include servers/slapd/back-ldap/back-ldap.h before this file!" +#endif /* SLAPD_LDAP_H */ + +#ifndef SLAPD_META_H +#define SLAPD_META_H + +#include "external.h" + +/* String rewrite library */ +#include "rewrite.h" + +LDAP_BEGIN_DECL + +struct slap_conn; +struct slap_op; + +struct metasingleconn { + int candidate; +#define META_NOT_CANDIDATE 0 +#define META_CANDIDATE 1 + + LDAP *ld; + char *bound_dn; + int bound; +#define META_UNBOUND 0 +#define META_BOUND 1 +#define META_ANONYMOUS 2 +}; + +struct metaconn { + struct slap_conn *conn; + struct rewrite_info *rwinfo; + + /* + * means that the connection is bound; + * of course only one target actually is ... + */ + int bound_target; + /* supersedes the connection stuff */ + struct metasingleconn **conns; +}; + +struct metatarget { + char *uri; + char *suffix;/* normalized suffix */ + char *binddn; + char *bindpw; + + struct rewrite_info *rwinfo; + + struct ldapmap oc_map; + struct ldapmap at_map; +}; + +struct metadncache { + ldap_pvt_thread_mutex_t mutex; + Avlnode *tree; + +#define META_DNCACHE_DISABLED 0 +#define META_DNCACHE_FOREVER -1 + long int ttl; /* seconds; 0: no cache, -1: no expiry */ +}; + +struct metainfo { + int ntargets; + int defaulttarget; +#define META_DEFAULT_TARGET_NONE -1 + struct metatarget **targets; + + struct metadncache cache; + + ldap_pvt_thread_mutex_t conn_mutex; + Avlnode *conntree; +}; + +extern int +meta_back_do_single_bind( + Operation *op, + struct metainfo *li, + struct metaconn *lc, + const char *dn, + const char *ndn, + struct berval *cred, + int method, + int candidate +); + + +#define META_OP_ALLOW_MULTIPLE 0x00 +#define META_OP_REQUIRE_SINGLE 0x01 +extern struct metaconn * +meta_back_getconn( + struct metainfo *li, + struct slap_conn *conn, + struct slap_op *op, + int op_type, + const char *dn, + int *candidate +); + +extern int +meta_back_dobind( + struct metaconn *lc, + Operation *op +); + +extern int +meta_back_op_result( + struct metaconn *lc, + Operation *op +); + +extern int +back_meta_LTX_init_module( + int argc, + char *argv[] +); + +extern int +meta_back_conn_cmp( + const void *c1, + const void *c2 +); + +extern int +meta_back_conn_dup( + void *c1, + void *c2 +); + +/* + * Candidate stuff + */ +extern int +meta_back_is_candidate( + const char *nsuffix, + const char *ndn, + int ndnlen +); + +extern int +meta_back_count_candidates( + struct metainfo *li, + const char *ndn +); + +extern int +meta_back_is_candidate_unique( + struct metainfo *li, + const char *ndn +); + +extern int +meta_back_select_unique_candidate( + struct metainfo *li, + const char *ndn +); + +extern int +meta_clear_unused_candidates( + struct metainfo *li, + struct metaconn *lc, + int candidate, + int reallyclean +); + +extern int +meta_clear_one_candidate( + struct metasingleconn *lc, + int reallyclean +); + +/* + * Dn cache stuff (experimental) + */ +extern int +meta_dncache_cmp( + const void *c1, + const void *c2 +); + +extern int +meta_dncache_dup( + void *c1, + void *c2 +); + +extern int +meta_dncache_get_target( + struct metadncache *cache, + const char *ndn +); + +extern int +meta_dncache_update_entry( + struct metadncache *cache, + const char *ndn, + int target +); + +extern int +meta_dncache_delete_entry( + struct metadncache *cache, + const char *ndn +); + +extern void +meta_dncache_free( + void *entry +); + +LDAP_END_DECL + +#endif /* SLAPD_META_H */ + diff --git a/servers/slapd/back-meta/bind.c b/servers/slapd/back-meta/bind.c new file mode 100644 index 0000000000..219e8b96d7 --- /dev/null +++ b/servers/slapd/back-meta/bind.c @@ -0,0 +1,356 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> + + +#define AVL_INTERNAL +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +int +meta_back_bind( + Backend *be, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn, + int method, + struct berval *cred, + char **edn +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + struct metaconn *lc; + + int rc = -1, i, gotit = 0, ndnlen, err = LDAP_SUCCESS; + +#ifdef NEW_LOGGING + LDAP_LOG(( "backend", LDAP_LEVEL_ENTRY, + "meta_back_bind: dn: %s.\n", dn )); +#else /* !NEW_LOGGING */ + Debug( LDAP_DEBUG_ARGS, "meta_back_bind: dn: %s.\n%s%s", dn, "", "" ); +#endif /* !NEW_LOGGING */ + + *edn = NULL; + + lc = meta_back_getconn( li, conn, op, META_OP_ALLOW_MULTIPLE, + ndn, NULL ); + if ( !lc ) { +#ifdef NEW_LOGGING + LDAP_LOG(( "backend", LDAP_LEVEL_NOTICE, + "meta_back_bind: no target for dn %s.\n", dn )); +#else /* !NEW_LOGGING */ + Debug( LDAP_DEBUG_ANY, + "meta_back_bind: no target for dn %s.\n%s%s", + dn, "", ""); +#endif /* !NEW_LOGGING */ + return -1; + } + + /* + * Each target is scanned ... + */ + ndnlen = strlen( ndn ); + for ( i = 0; i < li->ntargets; i++ ) { + int lerr; + + /* + * Skip non-candidates + */ + if ( lc->conns[ i ]->candidate != META_CANDIDATE ) { + continue; + } + + if ( gotit == 0 ) { + gotit = 1; + } else { + /* + * A bind operation is expected to have + * ONE CANDIDATE ONLY! + */ +#ifdef NEW_LOGGING + LDAP_LOG(( "backend", LDAP_LEVEL_WARNING, +"==>meta_back_bind: more that one candidate is attempting to bind ...%s%s%s\n" )); +#else /* !NEW_LOGGING */ + Debug( LDAP_DEBUG_ANY, +"==>meta_back_bind: more that one candidate is attempting to bind ...%s%s%s\n", + "", "", "" ); +#endif /* !NEW_LOGGING */ + } + + + lerr = meta_back_do_single_bind( op, li, lc, dn, ndn, cred, + method, i ); + if ( lerr != LDAP_SUCCESS ) { + err = lerr; + ( void )meta_clear_one_candidate( lc->conns[ i ], 1 ); + } else { + rc = LDAP_SUCCESS; + } + } + + if ( rc != LDAP_SUCCESS && err != LDAP_SUCCESS ) { + + /* + * deal with bind failure ... + */ + err = ldap_back_map_result( err ); + send_ldap_result( conn, op, err, NULL, "", NULL, NULL ); + } + + return LDAP_SUCCESS; +} + +/* + * meta_back_do_single_bind + * + * attempts to perform a bind with creds + */ +int +meta_back_do_single_bind( + Operation *op, + struct metainfo *li, + struct metaconn *lc, + const char *dn, + const char *ndn, + struct berval *cred, + int method, + int candidate +) +{ + char *mdn = NULL; + int rc; + + /* + * Rewrite the bind dn if needed + */ + switch ( rewrite_session( li->targets[ candidate ]->rwinfo, + "bindDn", dn, lc->conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, + "rw> bindDn: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( lc->conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + /* continues to next case */ + + case REWRITE_REGEXEC_ERR: + return -1; + } + + rc = ldap_bind_s( lc->conns[ candidate ]->ld, mdn, + cred->bv_val, method ); + if ( rc != LDAP_SUCCESS ) { + rc = ldap_back_map_result( rc ); + } else { + lc->conns[ candidate ]->bound_dn = ch_strdup( dn ); + lc->conns[ candidate ]->bound = META_BOUND; + lc->bound_target = candidate; + + if ( li->cache.ttl != META_DNCACHE_DISABLED + && ndn[ 0 ] != '\0' ) { + ( void )meta_dncache_update_entry( &li->cache, + ch_strdup( ndn ), candidate ); + } + } + + if ( mdn != dn ) { + free( mdn ); + } + + return rc; +} + +/* + * meta_back_dobind + */ +int +meta_back_dobind( struct metaconn *lc, Operation *op ) +{ + struct metasingleconn **lsc; + int bound = 0, i; + + for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) { + int rc; + + /* + * Not a candidate or something wrong with this target ... + */ + if ( lsc[ 0 ]->ld == NULL ) { + continue; + } + + /* + * If the target is already bound it is skipped + */ + if ( lsc[ 0 ]->bound == META_BOUND && lc->bound_target == i ) { + ++bound; + continue; + } + + /* + * Otherwise an anonymous bind is performed + * (note: if the target was already bound, the anonymous + * bind clears the previous bind). + */ + rc = ldap_bind_s( lsc[ 0 ]->ld, lsc[ 0 ]->bound_dn, + NULL, LDAP_AUTH_SIMPLE ); + if ( rc != LDAP_SUCCESS ) { + + /* + * This way, the first bind error would be fatal ... + */ + Debug( LDAP_DEBUG_ANY, + "==>meta_back_dobind: (anonymous) bind as \"%s\" failed" + " with error \"%s\"\n%s", + lsc[ 0 ]->bound_dn, + ldap_err2string( rc ), "" ); + + /* + * null cred bind should always succeed + * as anonymous, so a failure means + * the target is no longer candidate possibly + * due to technical reasons (remote host down?) + * + * so better clear the handle + */ + ( void )meta_clear_one_candidate( lsc[ 0 ], 1 ); + continue; + } /* else */ + + lsc[ 0 ]->bound = META_ANONYMOUS; + ++bound; + } + + return( bound > 0 ); +} + +/* + * FIXME: error return must be handled in a cleaner way ... + */ +int +meta_back_op_result( struct metaconn *lc, Operation *op ) +{ + int i, err = LDAP_SUCCESS; + char *msg = NULL; + char *match = NULL; + struct metasingleconn **lsc; + + for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) { + ldap_get_option( lsc[ 0 ]->ld, LDAP_OPT_ERROR_NUMBER, &err ); + if ( err != LDAP_SUCCESS ) { + /* + * better check the type of error. In some cases + * (search ?) it might be better to return a + * success if at least one of the targets gave + * positive result ... + */ + ldap_get_option( lsc[ 0 ]->ld, + LDAP_OPT_ERROR_STRING, &msg ); + ldap_get_option( lsc[ 0 ]->ld, + LDAP_OPT_MATCHED_DN, &match ); + err = ldap_back_map_result( err ); + + /* + * FIXME: need to rewrite "match" + */ + send_ldap_result( lc->conn, op, err, match, msg, + NULL, NULL ); + + Debug(LDAP_DEBUG_ANY, +"==> meta_back_op_result: target <%d> sending msg \"%s\" (matched \"%s\")\n", + i, + ( msg ? msg : "" ), + ( match ? match : "" ) ); + + /* better test the pointers before freeing? */ + if ( match ) { + free( match ); + } + if ( msg ) { + free( msg ); + } + return -1; + } + } + + return ( err == LDAP_SUCCESS ) ? 0 : -1; +} + diff --git a/servers/slapd/back-meta/candidates.c b/servers/slapd/back-meta/candidates.c new file mode 100644 index 0000000000..fe08ceda99 --- /dev/null +++ b/servers/slapd/back-meta/candidates.c @@ -0,0 +1,284 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +/* + * The meta-directory has one suffix, called <suffix>. + * It handles a pool of target servers, each with a branch suffix + * of the form <branch X>,<suffix> + * + * When the meta-directory receives a request with a dn that belongs + * to a branch, the corresponding target is invoked. When the dn + * does not belong to a specific branch, all the targets that + * are compatible with the dn are selected as candidates, and + * the request is spawned to all the candidate targets + * + * A request is characterized by a dn. The following cases are handled: + * - the dn is the suffix: <dn> == <suffix>, + * all the targets are candidates (search ...) + * - the dn is a branch suffix: <dn> == <branch X>,<suffix>, or + * - the dn is a subtree of a branch suffix: + * <dn> == <rdn>,<branch X>,<suffix>, + * the target is the only candidate. + * + * A possible extension will include the handling of multiple suffixes + */ + +/* + * returns 1 if suffix is candidate for dn, otherwise 0 + * + * Note: this function should never be called if dn is the <suffix>. + */ +int +meta_back_is_candidate( + const char *nsuffix, + const char *ndn, + int ndnlen +) +{ + int len = strlen( nsuffix ); + + if ( len > ndnlen ) { + /* + * suffix longer than dn + */ + if ( ! DN_SEPARATOR( nsuffix[ ( len - ndnlen ) - 1 ] ) ) { + /* + * not a separator begins the possible common part + */ + return META_NOT_CANDIDATE; + } + + if ( strcmp( &nsuffix[ len - ndnlen] , ndn ) == 0 ) { + /* + * Got it! + */ + return META_CANDIDATE; + } + } + + if ( len < ndnlen && ! DN_SEPARATOR( ndn[ ( ndnlen - len ) - 1 ] ) ) { + /* + * not a separator begins the possible common part + */ + return META_NOT_CANDIDATE; + } + + if ( strcmp( nsuffix, &ndn[ ndnlen - len ] ) == 0 ) { + /* + * Got it! + */ + return META_CANDIDATE; + } + + return META_NOT_CANDIDATE; +} + +/* + * meta_back_count_candidates + * + * returns a count of the possible candidate targets + * Note: dn MUST be normalized + */ + +int +meta_back_count_candidates( + struct metainfo *li, + const char *ndn +) +{ + int i, cnt = 0, ndnlen = strlen( ndn ); + + /* + * I know assertions should not check run-time values; + * at present I didn't find a place for such checks + * after config.c + */ + assert( li->targets != NULL ); + assert( li->ntargets != 0 ); + + for ( i = 0; i < li->ntargets; ++i ) { + if ( meta_back_is_candidate( li->targets[ i ]->suffix, + ndn, ndnlen ) ) { + ++cnt; + } + } + + return cnt; +} + +/* + * meta_back_is_candidate_unique + * + * checks whether a candidate is unique + * Note: dn MUST be normalized + */ +int +meta_back_is_candidate_unique( + struct metainfo *li, + const char *ndn +) +{ + return ( meta_back_count_candidates( li, ndn ) == 1 ); +} + +/* + * meta_back_select_unique_candidate + * + * returns the index of the candidate in case it is unique, otherwise -1 + * Note: dn MUST be normalized. + * Note: if defined, the default candidate is returned in case of no match. + */ +int +meta_back_select_unique_candidate( + struct metainfo *li, + const char *ndn +) +{ + int i, ndnlen; + + switch ( meta_back_count_candidates( li, ndn ) ) { + case 1: + break; + case 0: + default: + return ( li->defaulttarget == META_DEFAULT_TARGET_NONE + ? -1 : li->defaulttarget ); + } + + ndnlen = strlen( ndn ); + for ( i = 0; i < li->ntargets; ++i ) { + if ( meta_back_is_candidate( li->targets[ i ]->suffix, + ndn, ndnlen ) ) { + return i; + } + } + + return -1; +} + +/* + * meta_clear_unused_candidates + * + * clears all candidates except candidate + */ +int +meta_clear_unused_candidates( + struct metainfo *li, + struct metaconn *lc, + int candidate, + int reallyclean +) +{ + int i; + + for ( i = 0; i < li->ntargets; ++i ) { + if ( i == candidate ) { + continue; + } + meta_clear_one_candidate( lc->conns[ i ], reallyclean ); + } + + return 0; +} + +/* + * meta_clear_one_candidate + * + * clears the selected candidate + */ +int +meta_clear_one_candidate( + struct metasingleconn *lsc, + int reallyclean +) +{ + lsc->candidate = META_NOT_CANDIDATE; + + if ( !reallyclean ) { + return 0; + } + + if ( lsc->ld ) { + ldap_unbind( lsc->ld ); + lsc->ld = NULL; + } + + if ( lsc->bound_dn != NULL ) { + free( lsc->bound_dn ); + lsc->bound_dn = NULL; + } + + return 0; +} + diff --git a/servers/slapd/back-meta/compare.c b/servers/slapd/back-meta/compare.c new file mode 100644 index 0000000000..9ba3ac2936 --- /dev/null +++ b/servers/slapd/back-meta/compare.c @@ -0,0 +1,336 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +int +meta_back_compare( + Backend *be, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn, + AttributeAssertion *ava +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + struct metaconn *lc; + struct metasingleconn **lsc; + char *match = NULL, *err = NULL, *mmatch = NULL; + int candidates = 0, last = 0, i, count, rc, cres, rres; + int *msgid; + + lc = meta_back_getconn( li, conn, op, META_OP_ALLOW_MULTIPLE, + ndn, NULL ); + if ( !lc || !meta_back_dobind( lc, op ) ) { + return -1; + } + + msgid = ch_calloc( sizeof( int ), li->ntargets ); + if ( msgid == NULL ) { + return -1; + } + + /* + * start an asynchronous compare for each candidate target + */ + for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) { + char *mdn = NULL; + char *mapped_attr = ava->aa_desc->ad_cname->bv_val; + char *mapped_value = ava->aa_value->bv_val; + + if ( lsc[ 0 ]->candidate != META_CANDIDATE ) { + continue; + } + + /* + * Rewrite the compare dn, if needed + */ + switch ( rewrite_session( li->targets[ i ]->rwinfo, + "compareDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, + "rw> compareDn: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return -1; + } + + /* + * if attr is objectClass, try to remap the value + */ + if ( ava->aa_desc->ad_type->sat_oid + == slap_schema.si_ad_objectClass->ad_type->sat_oid ) { + mapped_value = ldap_back_map( &li->targets[ i ]->oc_map, + ava->aa_value->bv_val, 0 ); + + if ( mapped_value == NULL ) { + lsc[ 0 ]->candidate = META_NOT_CANDIDATE; + continue; + } + /* + * else try to remap the attribute + */ + } else { + mapped_attr = ldap_back_map( &li->targets[ i ]->at_map, + ava->aa_desc->ad_cname->bv_val, 0 ); + if ( mapped_attr == NULL ) { + lsc[ 0 ]->candidate = META_NOT_CANDIDATE; + continue; + } + } + + /* + * the compare op is spawned across the targets and the first + * that returns determines the result; a constraint on unicity + * of the result ought to be enforced + */ + msgid[ i ] = ldap_compare( lc->conns[ i ]->ld, mdn, + mapped_attr, mapped_value ); + if ( msgid[ i ] == -1 ) { + lsc[ 0 ]->candidate = META_NOT_CANDIDATE; + continue; + } + + if ( mdn != dn ) { + free( mdn ); + } + if ( mapped_attr != ava->aa_desc->ad_cname->bv_val ) { + free( mapped_attr ); + } + if ( mapped_value != ava->aa_value->bv_val ) { + free( mapped_value ); + } + + ++candidates; + } + + /* + * wait for replies + */ + for ( rc = 0, count = 0; candidates > 0; ) { + + /* + * FIXME: should we check for abandon? + */ + for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; lsc++, i++ ) { + int lrc; + LDAPMessage *res = NULL; + + if ( lsc[ 0 ]->candidate != META_CANDIDATE ) { + continue; + } + + lrc = ldap_result( lsc[ 0 ]->ld, msgid[ i ], + 0, NULL, &res ); + + if ( lrc == 0 ) { + /* + * FIXME: should we yield? + */ + if ( res ) { + ldap_msgfree( res ); + } + continue; + } else if ( lrc == LDAP_RES_COMPARE ) { + if ( count > 0 ) { + rres = LDAP_OPERATIONS_ERROR; + rc = -1; + goto finish; + } + + cres = ldap_result2error( lsc[ 0 ]->ld, + res, 1 ); + switch ( cres ) { + case LDAP_COMPARE_TRUE: + case LDAP_COMPARE_FALSE: + + /* + * true or flase, got it; + * sending to cache ... + */ + if ( li->cache.ttl != META_DNCACHE_DISABLED ) { + ( void )meta_dncache_update_entry( &li->cache, ch_strdup( ndn ), i ); + } + + count++; + rc = 0; + break; + + default: + rres = ldap_back_map_result( cres ); + + if ( err != NULL ) { + free( err ); + } + ldap_get_option( lsc[ 0 ]->ld, + LDAP_OPT_ERROR_STRING, &err ); + + if ( match != NULL ) { + free( match ); + } + ldap_get_option( lsc[ 0 ]->ld, + LDAP_OPT_MATCHED_DN, &match ); + + last = i; + break; + } + lsc[ 0 ]->candidate = META_NOT_CANDIDATE; + --candidates; + } else { + lsc[ 0 ]->candidate = META_NOT_CANDIDATE; + --candidates; + if ( res ) { + ldap_msgfree( res ); + } + break; + } + } + } + +finish: + + /* + * Rewrite the matched portion of the search base, if required + * + * FIXME: only the last one gets caught! + */ + if ( count == 1 ) { + if ( match != NULL ) { + free( match ); + match = NULL; + } + + /* + * the result of the compare is assigned to the res code + * that will be returned + */ + rres = cres; + + } else if ( match != NULL ) { + + /* + * At least one compare failed with matched portion, + * and none was successful + */ + switch ( rewrite_session( li->targets[ last ]->rwinfo, + "matchedDn", match, conn, &mmatch ) ) { + case REWRITE_REGEXEC_OK: + if ( mmatch == NULL ) { + mmatch = ( char * )match; + } + Debug( LDAP_DEBUG_ARGS, "rw> matchedDn:" + " \"%s\" -> \"%s\"\n%s", + match, mmatch, "" ); + break; + + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + /* continue to the next case */ + + case REWRITE_REGEXEC_ERR: + rc = -1; + break; + } + } + send_ldap_result( conn, op, rres, mmatch, err, NULL, NULL ); + + if ( match != NULL ) { + if ( mmatch != match ) { + free( mmatch ); + } + free( match ); + } + + if ( msgid ) { + free( msgid ); + } + + return rc; +} + diff --git a/servers/slapd/back-meta/config.c b/servers/slapd/back-meta/config.c new file mode 100644 index 0000000000..7c449b200e --- /dev/null +++ b/servers/slapd/back-meta/config.c @@ -0,0 +1,519 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +extern int +suffix_massage_config( + struct rewrite_info *info, + int argc, + char **argv +); + +static struct metatarget * +new_target( void ) +{ + struct metatarget *lt; + struct ldapmapping *mapping; + + lt = ch_calloc( sizeof( struct metatarget ), 1 ); + if ( lt == NULL ) { + return NULL; + } + + lt->rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT ); + if ( lt->rwinfo == NULL ) { + free( lt ); + return NULL; + } + + mapping = ch_calloc( 2, sizeof( struct ldapmapping ) ); + if ( mapping == NULL ) { + free( lt ); + return NULL; + } + mapping->src = ch_strdup( "objectClass" ); + mapping->dst = ch_strdup( "objectClass" ); + mapping[ 1 ].src = mapping->src; + mapping[ 1 ].dst = mapping->dst; + + avl_insert( &lt->at_map.map, ( caddr_t )mapping, + mapping_cmp, mapping_dup ); + avl_insert( &lt->at_map.remap, ( caddr_t )&mapping[ 1 ], + mapping_cmp, mapping_dup ); + + return lt; +} + +int +meta_back_db_config( + BackendDB *be, + const char *fname, + int lineno, + int argc, + char **argv +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + + if ( li == NULL ) { + fprintf( stderr, + "%s: line %d: meta backend info is null!\n", + fname, lineno ); + return 1; + } + + /* URI of server to query */ + if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) { + int j, i = li->ntargets; + LDAPURLDesc *ludp; + char *last; + + if ( argc != 2 ) { + fprintf( stderr, + "%s: line %d: missing address" + " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n", + fname, lineno ); + return 1; + } + + ++li->ntargets; + + li->targets = ch_realloc( li->targets, + sizeof( struct metatarget *)*li->ntargets ); + if ( li->targets == NULL ) { + fprintf( stderr, + "%s: line %d: out of memory while storing server name" + " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n", + fname, lineno ); + return 1; + } + + if ( ( li->targets[ i ] = new_target() ) == NULL ) { + fprintf( stderr, + "%s: line %d: unable to init server" + " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n", + fname, lineno ); + return 1; + } + + /* + * uri MUST be legal! + */ + if ( ldap_url_parse( argv[ 1 ], &ludp ) != LDAP_SUCCESS ) { + fprintf( stderr, + "%s: line %d: unable to parse URI" + " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n", + fname, lineno ); + return 1; + } + + /* + * uri MUST have the <dn> part! + */ + if ( ludp->lud_dn == NULL || ludp->lud_dn[ 0 ] == '\0' ) { + fprintf( stderr, + "%s: line %d: missing <naming context> " + " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n", + fname, lineno ); + return 1; + } + + /* + * copies and stores uri and suffix + */ + li->targets[ i ]->suffix = ch_strdup( ludp->lud_dn ); + li->targets[ i ]->uri = ch_strdup( argv[ 1 ] ); + last = strstr( li->targets[ i ]->uri, + li->targets[ i ]->suffix ); + assert( last != NULL ); + last[ 0 ] = '\0'; /* wasting memory ... */ + + /* + * Need to store the suffix in normalized form + */ + (void) dn_normalize( li->targets[ i ]->suffix ); + + /* + * uri MUST be a branch of suffix! + */ +#if 0 /* too strict a constraint */ + if ( select_backend( li->targets[ i ]->suffix, 0 ) != be ) { + fprintf( stderr, + "%s: line %d: <naming context> of URI does not refer to current backend" + " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n", + fname, lineno ); + return 1; + } +#else + /* + * uri MUST be a branch of a suffix! + */ + if ( select_backend( li->targets[ i ]->suffix, 0 ) == NULL ) { + fprintf( stderr, + "%s: line %d: <naming context> of URI does not resolve to a backend" + " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n", + fname, lineno ); + return 1; + } +#endif + +#if 0 + /* + * uri MUST not be used by other URIs! + * + * FIXME: this limitation may be removed, + * or worked out, at least, in some manner + */ + for ( j = 0; j < i-1; j++ ) { + if ( strcmp( li->targets[ i ]->suffix, + li->targets[ j ]->suffix ) == 0 ) { + fprintf( stderr, + "%s: line %d: naming context \"%s\" already used" + " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n", + fname, lineno, last+1 ); + return 1; + } + } +#endif + + ldap_free_urldesc( ludp ); + + Debug( LDAP_DEBUG_CONFIG, + "==>meta_back_db_config: URI \"%s\", suffix \"%s\"%s\n", + li->targets[ i ]->uri, li->targets[ i ]->suffix, "" ); + + /* default target directive */ + } else if ( strcasecmp( argv[ 0 ], "default-target" ) == 0 ) { + int i = li->ntargets-1; + + if ( argc == 1 ) { + if ( i < 0 ) { + fprintf( stderr, + "%s: line %d: \"default-target\" alone need be" + " inside a \"uri\" directive\n", + fname, lineno ); + return 1; + } + li->defaulttarget = i; + } else { + if ( strcasecmp( argv[ 1 ], "none" ) == 0 ) { + if ( i >= 0 ) { + fprintf( stderr, + "%s: line %d: \"default-target none\"" + " should go before uri definitions\n", + fname, lineno ); + } + li->defaulttarget = META_DEFAULT_TARGET_NONE; + } else { + int n = atoi( argv[ 1 ] ); + if ( n < 1 || n >= i ) { + fprintf( stderr, + "%s: line %d: illegal target number %d\n", + fname, lineno, n ); + return 1; + } + li->defaulttarget = n-1; + } + } + + /* ttl of dn cache */ + } else if ( strcasecmp( argv[ 0 ], "dncache-ttl" ) == 0 ) { + if ( argc != 2 ) { + fprintf( stderr, + "%s: line %d: missing ttl in \"dncache-ttl <ttl>\" line\n", + fname, lineno ); + return 1; + } + + if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) { + li->cache.ttl = META_DNCACHE_FOREVER; + } else if ( strcasecmp( argv[ 1 ], "disabled" ) == 0 ) { + li->cache.ttl = META_DNCACHE_DISABLED; + } else { + li->cache.ttl = atol( argv[ 1 ] ); + } + + /* name to use for meta_back_group */ + } else if ( strcasecmp( argv[ 0 ], "binddn" ) == 0 ) { + int i = li->ntargets-1; + + if ( i < 0 ) { + fprintf( stderr, + "%s: line %d: need \"uri\" directive first\n", + fname, lineno ); + } + + if ( argc != 2 ) { + fprintf( stderr, + "%s: line %d: missing name in \"binddn <name>\" line\n", + fname, lineno ); + return 1; + } + li->targets[ i ]->binddn = ch_strdup( argv[ 1 ] ); + + /* password to use for meta_back_group */ + } else if ( strcasecmp( argv[ 0 ], "bindpw" ) == 0 ) { + int i = li->ntargets-1; + + if ( i < 0 ) { + fprintf( stderr, + "%s: line %d: need \"uri\" directive first\n", + fname, lineno ); + } + + if ( argc != 2 ) { + fprintf( stderr, + "%s: line %d: missing password in \"bindpw <password>\" line\n", + fname, lineno ); + return 1; + } + li->targets[ i ]->bindpw = ch_strdup( argv[ 1 ] ); + + /* dn massaging */ + } else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) { + BackendDB *tmp_be; + int i = li->ntargets-1; + + if ( i < 0 ) { + fprintf( stderr, + "%s: line %d: need \"uri\" directive first\n", + fname, lineno ); + return 1; + } + + /* + * syntax: + * + * suffixmassage <suffix> <massaged suffix> + * + * the <suffix> field must be defined as a valid suffix + * (or suffixAlias?) for the current database; + * the <massaged suffix> shouldn't have already been + * defined as a valid suffix or suffixAlias for the + * current server + */ + if ( argc != 3 ) { + fprintf( stderr, + "%s: line %d: syntax is \"suffixMassage <suffix> <massaged suffix>\"\n", + fname, lineno ); + return 1; + } + + tmp_be = select_backend( argv[ 1 ], 0 ); + if ( tmp_be != NULL && tmp_be != be ) { + fprintf( stderr, + "%s: line %d: suffix already in use by another backend in" + " \"suffixMassage <suffix> <massaged suffix>\"\n", + fname, lineno ); + return 1; + } + + tmp_be = select_backend( argv[ 2 ], 0 ); + if ( tmp_be != NULL ) { + fprintf( stderr, + "%s: line %d: massaged suffix already in use by another backend in" + " \"suffixMassage <suffix> <massaged suffix>\"\n", + fname, lineno ); + return 1; + } + + /* + * The suffix massaging is emulated by means of the + * rewrite capabilities + * FIXME: no extra rewrite capabilities should be added + * to the database + */ + return suffix_massage_config( li->targets[ i ]->rwinfo, + argc, argv ); + + /* rewrite stuff ... */ + } else if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) { + int i = li->ntargets-1; + + if ( i < 0 ) { + fprintf( stderr, + "%s: line %d: need \"uri\" directive first\n", + fname, lineno ); + } + + return rewrite_parse( li->targets[ i ]->rwinfo, fname, lineno, + argc, argv ); + + /* objectclass/attribute mapping */ + } else if ( strcasecmp( argv[ 0 ], "map" ) == 0 ) { + struct ldapmap *map; + struct ldapmapping *mapping; + char *src, *dst; + int i = li->ntargets-1; + + if ( i < 0 ) { + fprintf( stderr, + "%s: line %d: need \"uri\" directive first\n", + fname, lineno ); + } + + + if ( argc < 3 || argc > 4 ) { + fprintf( stderr, + "%s: line %d: syntax is \"map {objectclass | attribute} {<source> | *} [<dest> | *]\"\n", + fname, lineno ); + return 1; + } + + if ( strcasecmp( argv[ 1 ], "objectClass" ) == 0 ) { + map = &li->targets[ i ]->oc_map; + } else if ( strcasecmp( argv[ 1 ], "attribute" ) == 0 ) { + map = &li->targets[ i ]->at_map; + } else { + fprintf( stderr, + "%s: line %d: syntax is \"map {objectclass | attribute} {<source> | *} [<dest> | *]\"\n", + fname, lineno ); + return 1; + } + + if ( strcasecmp( argv[ 2 ], "*" ) != 0 ) { + src = argv[ 2 ]; + if ( argc < 4 ) { + dst = ""; + } else if ( strcasecmp( argv[ 3 ], "*" ) == 0 ) { + dst = src; + } else { + dst = argv[ 3 ]; + } + } else { + if ( argc < 4 ) { + map->drop_missing = 1; + return 0; + } + if ( strcasecmp( argv[ 3 ], "*" ) == 0 ) { + map->drop_missing = 0; + return 0; + } + + src = argv[ 3 ]; + dst = src; + } + + if ( ( map == &li->targets[ i ]->at_map ) + && ( strcasecmp( src, "objectclass" ) == 0 + || strcasecmp( dst, "objectclass" ) == 0 ) ) { + fprintf( stderr, + "%s: line %d: objectclass attribute cannot be mapped\n", + fname, lineno ); + } + + mapping = ch_calloc( 2, sizeof( struct ldapmapping ) ); + if ( mapping == NULL ) { + fprintf( stderr, + "%s: line %d: out of memory\n", + fname, lineno ); + return 1; + } + mapping->src = ch_strdup( src ); + mapping->dst = ch_strdup( dst ); + if ( *dst != 0 ) { + mapping[ 1 ].src = mapping->dst; + mapping[ 1 ].dst = mapping->src; + } else { + mapping[ 1 ].src = mapping->src; + mapping[ 1 ].dst = mapping->dst; + } + + if ( avl_find( map->map, ( caddr_t )mapping, + mapping_cmp ) != NULL + || avl_find( map->remap, ( caddr_t )&mapping[ 1 ], + mapping_cmp ) != NULL) { + fprintf( stderr, + "%s: line %d: duplicate mapping found (ignored)\n", + fname, lineno ); + return 0; + } + + avl_insert( &map->map, ( caddr_t )mapping, + mapping_cmp, mapping_dup ); + avl_insert( &map->remap, ( caddr_t )&mapping[ 1 ], + mapping_cmp, mapping_dup ); + + /* anything else */ + } else { + fprintf( stderr, + "%s: line %d: unknown directive \"%s\" in meta database definition" + " (ignored)\n", + fname, lineno, argv[0] ); + } + return 0; +} + diff --git a/servers/slapd/back-meta/conn.c b/servers/slapd/back-meta/conn.c new file mode 100644 index 0000000000..cf440dec08 --- /dev/null +++ b/servers/slapd/back-meta/conn.c @@ -0,0 +1,501 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> + + +#define AVL_INTERNAL +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +/* + * Set PRINT_CONNTREE larger than 0 to dump the connection tree (debug only) + */ +#define PRINT_CONNTREE 0 + +/* + * meta_back_conn_cmp + * + * compares two struct metaconn based on the value of the conn pointer; + * used by avl stuff + */ +int +meta_back_conn_cmp( + const void *c1, + const void *c2 + ) +{ + struct metaconn *lc1 = ( struct metaconn * )c1; + struct metaconn *lc2 = ( struct metaconn * )c2; + + return ( ( lc1->conn < lc2->conn ) ? -1 : + ( ( lc1->conn > lc2-> conn ) ? 1 : 0 ) ); +} + +/* + * meta_back_conn_dup + * + * returns -1 in case a duplicate struct metaconn has been inserted; + * used by avl stuff + */ +int +meta_back_conn_dup( + void *c1, + void *c2 + ) +{ + struct metaconn *lc1 = ( struct metaconn * )c1; + struct metaconn *lc2 = ( struct metaconn * )c2; + + return( ( lc1->conn == lc2->conn ) ? -1 : 0 ); +} + +/* + * Debug stuff (got it from libavl) + */ +#if PRINT_CONNTREE > 0 +static void +ravl_print( Avlnode *root, int depth ) +{ + int i; + + if ( root == 0 ) { + return; + } + + ravl_print( root->avl_right, depth+1 ); + + for ( i = 0; i < depth; i++ ) { + printf( " " ); + } + + printf( "c(%d) %d\n", ( ( struct metaconn * )root->avl_data )->conn->c_connid, root->avl_bf ); + + ravl_print( root->avl_left, depth+1 ); +} + +static void +myprint( Avlnode *root ) +{ + printf( "********\n" ); + + if ( root == 0 ) { + printf( "\tNULL\n" ); + } else { + ravl_print( root, 0 ); + } + + printf( "********\n" ); +} +#endif /* PRINT_CONNTREE */ +/* + * End of debug stuff + */ + +/* + * metaconn_alloc + * + * Allocates a connection structure, making room for all the referenced targets + */ +static struct metaconn * +metaconn_alloc( int ntargets ) +{ + struct metaconn *lc; + int i; + + assert( ntargets > 0 ); + + lc = ch_calloc( sizeof( struct metaconn ), 1 ); + if ( lc == NULL ) { + return NULL; + } + + /* + * make it a null-terminated array ... + */ + lc->conns = ch_calloc( sizeof( struct metasingleconn * ), ntargets+1 ); + if ( lc->conns == NULL ) { + free( lc ); + return NULL; + } + + for ( i = 0; i < ntargets; i++ ) { + lc->conns[ i ] = + ch_calloc( sizeof( struct metasingleconn ), 1 ); + if ( lc->conns[ i ] == NULL ) { + charray_free( ( char ** )lc->conns ); + free( lc->conns ); + free( lc ); + return NULL; + } + } + + lc->bound_target = -1; + + return lc; +} + +/* + * metaconn_free + * + * clears a metaconn + */ +static void +metaconn_free( + struct metaconn *lc +) +{ + if ( !lc ) { + return; + } + + if ( lc->conns ) { + int i; + + for ( i = 0; lc->conns[ i ] != NULL; ++i ) { + free( lc->conns[ i ] ); + } + charray_free( ( char ** )lc->conns ); + } + + free( lc ); +} + +/* + * init_one_conn + * + * Initializes one connection + */ +static int +init_one_conn( + Connection *conn, + Operation *op, + struct metatarget *lt, + int vers, + struct metasingleconn *lsc + ) +{ + int err; + + /* + * Already init'ed + */ + if ( lsc->ld != NULL ) { + return LDAP_SUCCESS; + } + + /* + * Attempts to initialize the connection to the target ds + */ + err = ldap_initialize( &lsc->ld, lt->uri ); + + /* + * In case of failure, the error is mapped back from client + * to server error code + */ + if ( err != LDAP_SUCCESS ) { + return ldap_back_map_result( err ); + } + + /* + * Set LDAP version. This will always succeed: If the client + * bound with a particular version, then so can we. + */ + ldap_set_option( lsc->ld, LDAP_OPT_PROTOCOL_VERSION, &vers ); + + /* + * Sets a cookie for the rewrite session + */ + ( void )rewrite_session_init( lt->rwinfo, conn ); + + /* + * If the connection dn is not null, an attempt to rewrite it is made + */ + if ( conn->c_cdn != NULL && conn->c_cdn[ 0 ] != '\0' ) { + /* + * Rewrite the bind dn if needed + */ + lsc->bound_dn = NULL; + switch ( rewrite_session( lt->rwinfo, "bindDn", + conn->c_cdn, conn, + &lsc->bound_dn ) ) { + case REWRITE_REGEXEC_OK: + if ( lsc->bound_dn == NULL ) { + lsc->bound_dn = ch_strdup( conn->c_cdn ); + } + Debug( LDAP_DEBUG_ARGS, + "rw> bindDn: \"%s\" -> \"%s\"\n%s", + conn->c_cdn, lsc->bound_dn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, + LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + /* continues to the next case */ + + case REWRITE_REGEXEC_ERR: + return LDAP_OPERATIONS_ERROR; + } + } else { + lsc->bound_dn = NULL; + } + + lsc->bound = META_UNBOUND; + + /* + * The candidate is activated + */ + lsc->candidate = META_CANDIDATE; + return LDAP_SUCCESS; +} + +/* + * meta_back_getconn + * + * Prepares the connection structure + * + * FIXME: This function needs to receive some info on the type of operation + * it is invoked by, so that only the correct pool of candidate targets + * is initialized in case no connection was available yet. + * + * At present a flag that says whether the candidate target must be unique + * is passed; eventually an operation agent will be used. + */ +struct metaconn * +meta_back_getconn( + struct metainfo *li, + Connection *conn, + Operation *op, + int op_type, + const char *ndn, + int *candidate + ) +{ + struct metaconn *lc, lc_curr; + int vers, cached = -1, i = -1, err = LDAP_SUCCESS; + int new_conn = 0; + + /* Searches for a metaconn in the avl tree */ + lc_curr.conn = conn; + ldap_pvt_thread_mutex_lock( &li->conn_mutex ); + lc = (struct metaconn *)avl_find( li->conntree, + (caddr_t)&lc_curr, meta_back_conn_cmp ); + ldap_pvt_thread_mutex_unlock( &li->conn_mutex ); + + /* Looks like we didn't get a bind. Open a new session... */ + if ( !lc ) { + lc = metaconn_alloc( li->ntargets ); + lc->conn = conn; + new_conn = 1; + } + + vers = conn->c_protocol; + + /* + * looks in cache, if any + */ + if ( li->cache.ttl != META_DNCACHE_DISABLED ) { + cached = i = meta_dncache_get_target( &li->cache, ndn ); + } + + if ( op_type == META_OP_REQUIRE_SINGLE ) { + + /* + * tries to get a unique candidate + * (takes care of default target + */ + if ( i < 0 ) { + i = meta_back_select_unique_candidate( li, ndn ); + } + + /* + * if any is found, inits the connection + */ + if ( i < 0 ) { + if ( new_conn ) { + metaconn_free( lc ); + } + + send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, + NULL, "", NULL, NULL ); + + return NULL; + } + + + Debug( LDAP_DEBUG_CACHE, + "==>meta_back_getconn: got target %d for ndn=\"%s\" from cache\n%s", + i, ndn, "" ); + + /* + * Clear all other candidates + */ + ( void )meta_clear_unused_candidates( li, lc, i, 0 ); + + /* + * The target is activated; if needed, it is + * also init'd + */ + err = init_one_conn( conn, op, li->targets[ i ], + vers, lc->conns[ i ] ); + if ( err != LDAP_SUCCESS ) { + + /* + * FIXME: in case one target cannot + * be init'd, should the other ones + * be tried? + */ + ( void )meta_clear_one_candidate( lc->conns[ i ], 1 ); + if ( new_conn ) { + metaconn_free( lc ); + } + + send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL, "internal server error", NULL, NULL ); + + return NULL; + } + + if ( candidate ) { + *candidate = i; + } + + /* + * if no unique candidate ... + */ + } else { + int ndnlen = strlen( ndn ); + for ( i = 0; i < li->ntargets; i++ ) { + if ( i == cached + || meta_back_is_candidate( li->targets[ i ]->suffix, + ndn, ndnlen ) ) { + + /* + * The target is activated; if needed, it is + * also init'd + */ + int lerr = init_one_conn( conn, op, + li->targets[ i ], + vers, lc->conns[ i ] ); + if ( lerr != LDAP_SUCCESS ) { + + /* + * FIXME: in case one target cannot + * be init'd, should the other ones + * be tried? + */ + ( void )meta_clear_one_candidate( lc->conns[ i ], 1 ); + err = lerr; + continue; + } + } + } + } + + if ( new_conn ) { + + /* + * Inserts the newly created metaconn in the avl tree + */ + ldap_pvt_thread_mutex_lock( &li->conn_mutex ); + err = avl_insert( &li->conntree, ( caddr_t )lc, + meta_back_conn_cmp, meta_back_conn_dup ); + +#if PRINT_CONNTREE > 0 + myprint( li->conntree ); +#endif /* PRINT_CONNTREE */ + + ldap_pvt_thread_mutex_unlock( &li->conn_mutex ); + + Debug( LDAP_DEBUG_TRACE, + "=>meta_back_getconn: conn %ld inserted\n%s%s", + lc->conn->c_connid, "", "" ); + + /* + * Err could be -1 in case a duplicate metaconn is inserted + */ + if ( err != 0 ) { + send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, + NULL, "internal server error", NULL, NULL ); + metaconn_free( lc ); + return NULL; + } + } else { + Debug( LDAP_DEBUG_TRACE, + "=>meta_back_getconn: conn %ld fetched\n%s%s", + lc->conn->c_connid, "", "" ); + } + + return lc; +} + diff --git a/servers/slapd/back-meta/data/meta-1.ldif b/servers/slapd/back-meta/data/meta-1.ldif new file mode 100644 index 0000000000..1f904653df --- /dev/null +++ b/servers/slapd/back-meta/data/meta-1.ldif @@ -0,0 +1,10 @@ +dn: ou=People, dc=foo, dc=example, dc=com +objectClass: top + +dn: cn=Ando, ou=People, dc=foo, dc=example, dc=com +objectClass: top +objectClass: person +cn: Ando +sn: Ando +userPassword: ando + diff --git a/servers/slapd/back-meta/data/meta-2.ldif b/servers/slapd/back-meta/data/meta-2.ldif new file mode 100644 index 0000000000..7c717eeea7 --- /dev/null +++ b/servers/slapd/back-meta/data/meta-2.ldif @@ -0,0 +1,10 @@ +dn: ou=People, dc=bar, dc=example, dc=com +objectClass: top + +dn: cn=Ando, ou=People, dc=bar, dc=example, dc=com +objectClass: top +objectClass: person +cn: Ando +sn: Ando +userPassword: ando + diff --git a/servers/slapd/back-meta/data/meta-3.ldif b/servers/slapd/back-meta/data/meta-3.ldif new file mode 100644 index 0000000000..1786b24f01 --- /dev/null +++ b/servers/slapd/back-meta/data/meta-3.ldif @@ -0,0 +1,11 @@ +dn: ou=Groups, dc=bar, dc=example, dc=com +objectClass: top + +dn: cn=Users, ou=Groups, dc=bar, dc=example, dc=com +objectClass: top +objectClass: groupOfNames +objectClass: simpleSecurityObject +cn: Users +member: cn=Users, ou=Groups, dc=bar, dc=example, dc=com +member: cn=Ando, ou=People, dc=bar, dc=example, dc=com +userPassword: users diff --git a/servers/slapd/back-meta/data/setup.sh b/servers/slapd/back-meta/data/setup.sh new file mode 100644 index 0000000000..e9a8f3dbb4 --- /dev/null +++ b/servers/slapd/back-meta/data/setup.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +SRCDIR="../../../.." +METADBDIR="./meta-db" +SLAPADD="$SRCDIR/servers/slapd/tools/slapadd -v" + +ADDCONF="./slapd-meta-plain.conf" +#ADDCONF="./slapd-meta-rewrite.conf" +#ADDCONF="./slapd-ldap-rewrite.conf" + +LDAPADDCONF="./slapd-ldap-raw.conf" +CONF="./slapd.conf" +LDAPCONF="./slapd-ldap.conf" +PORT=9876 +#DEBUG=-1 +DEBUG=0 + +rm -rf $METADBDIR +rm -f schema ucdata +ln -s "$SRCDIR/servers/slapd/schema" . +ln -s "$SRCDIR/libraries/liblunicode" ucdata +for i in 1 2 3 ; do + echo "Feeding directory $i" + mkdir -p "$METADBDIR/$i" + $SLAPADD -f $ADDCONF -n $i -l meta-$i.ldif +done + +sed "s/@PORT@/$PORT/" $ADDCONF > $CONF +sed "s/@PORT@/$PORT/" $LDAPADDCONF > $LDAPCONF + +echo "" +echo "After slapd started, try" +echo "" +echo " ldapsearch -x -H ldap://localhost:$PORT -b '' -s base namingContexts" +echo "" +echo "and browse the directory using the last base that appears;" +echo "you may also try to bind as administrator of each subdirectory" +echo "or as \"cn=Ando, ...\" with password \"ando\": notice what happens" +echo "to attrs \"sn\" and \"cn\" of some entries based on the ACLs ..." +echo "" + +echo "Starting slapd on port $PORT" +$SRCDIR/servers/slapd/slapd -f $CONF -h "ldap://localhost:$PORT/" -d $DEBUG +echo "Waiting 2 secs for everything to shut down ..." +sleep 2 + +#exit + +rm -rf $METADBDIR +rm -f schema ucdata $CONF $LDAPCONF + diff --git a/servers/slapd/back-meta/data/slapd-ldap-raw.conf b/servers/slapd/back-meta/data/slapd-ldap-raw.conf new file mode 100644 index 0000000000..d83decac05 --- /dev/null +++ b/servers/slapd/back-meta/data/slapd-ldap-raw.conf @@ -0,0 +1,15 @@ +####################################################################### +# ldap database with suffix massage definitions +####################################################################### + +database ldap +uri "ldap://localhost:@PORT@/" +suffix "o=FB, c=US" +suffixmassage "o=FB, c=US" "ou=Groups, dc=bar, dc=example, dc=com" +lastmod off + +access to dn.regex="[^,]+,o=FB,c=US" attr=cn + by group.exact="cn=Users,o=FB,c=US" read + by group.exact="cn=Users,ou=Groups,dc=bar,dc=example,dc=com" read + by * none + diff --git a/servers/slapd/back-meta/data/slapd-ldap-rewrite.conf b/servers/slapd/back-meta/data/slapd-ldap-rewrite.conf new file mode 100644 index 0000000000..23b779a032 --- /dev/null +++ b/servers/slapd/back-meta/data/slapd-ldap-rewrite.conf @@ -0,0 +1,44 @@ +# +# master slapd config -- for testing of ldap metadirectory rewrite +# +ucdata-path ./ucdata +include ./schema/core.schema +include ./schema/cosine.schema +include ./schema/inetorgperson.schema +# +schemacheck off +# +pidfile ./meta-db/slapd.pid +argsfile ./meta-db/slapd.args + +access to attr=userPassword + by anonymous auth + by self write + +access to dn.regex="[^,]+,ou=People,dc=[^,]+,o=Foo Bar,c=US" attr=sn + by group.exact="cn=Users,ou=Groups,dc=bar,o=Foo Bar,c=US" read + by * none + +access to dn.regex="[^,]+,ou=Groups,dc=[^,]+,o=Foo Bar,c=US" attr=cn + by group.exact="cn=Users,ou=Groups,dc=bar,o=Foo Bar,c=US" read + by * none + +#access to dn.regex="[^,]+,ou=Groups,dc=[^,]+,o=Foo Bar,c=US" attr=cn +# by dnattr=member read +# by * none + +access to * + by * read + +####################################################################### +# ldbm database definitions +####################################################################### + +include ./slapd-ldbm.conf + +####################################################################### +# ldap database with suffix massage definitions +####################################################################### + +include ./slapd-ldap.conf + diff --git a/servers/slapd/back-meta/data/slapd-ldbm.conf b/servers/slapd/back-meta/data/slapd-ldbm.conf new file mode 100644 index 0000000000..8ca31453ab --- /dev/null +++ b/servers/slapd/back-meta/data/slapd-ldbm.conf @@ -0,0 +1,30 @@ +# +# slapd config -- for testing of ldap metadirectory +# + +####################################################################### +# ldbm database definitions +####################################################################### + +database ldbm +suffix "ou=People, dc=foo, dc=example, dc=com" +rootdn "cn=Root, ou=People, dc=foo, dc=example, dc=com" +rootpw ldap +directory ./meta-db/1 +lastmod on +index objectClass pres,eq + +database ldbm +suffix "ou=People, dc=bar, dc=example, dc=com" +rootdn "cn=Root, ou=People, dc=bar, dc=example, dc=com" +rootpw ldap +directory ./meta-db/2 +index objectClass pres,eq + +database ldbm +suffix "ou=Groups, dc=bar, dc=example, dc=com" +rootdn "cn=Root, ou=Groups, dc=bar, dc=example, dc=com" +rootpw ldap +directory ./meta-db/3 +index objectClass pres,eq + diff --git a/servers/slapd/back-meta/data/slapd-meta-plain.conf b/servers/slapd/back-meta/data/slapd-meta-plain.conf new file mode 100644 index 0000000000..06f27c4a91 --- /dev/null +++ b/servers/slapd/back-meta/data/slapd-meta-plain.conf @@ -0,0 +1,43 @@ +# +# master slapd config -- for testing of ldap metadirectory +# +ucdata-path ./ucdata +include ./schema/core.schema +include ./schema/cosine.schema +include ./schema/inetorgperson.schema +# +schemacheck off +# +pidfile ./meta-db/slapd.pid +argsfile ./meta-db/slapd.args + +access to attr=userPassword + by anonymous auth + by self write + +access to * + by * read + +####################################################################### +# ldbm database definitions +####################################################################### + +include ./slapd-ldbm.conf + +####################################################################### +# meta database definitions +####################################################################### + +database meta +suffix "dc=example, dc=com" +dncache-ttl forever +uri "ldap://localhost:@PORT@/ou=People, dc=foo, dc=example, dc=com" +uri "ldap://localhost:@PORT@/ou=People, dc=bar, dc=example, dc=com" +uri "ldap://localhost:@PORT@/ou=Groups, dc=bar, dc=example, dc=com" + +####################################################################### +# ldap database with suffix massage definitions +####################################################################### + +include ./slapd-ldap.conf + diff --git a/servers/slapd/back-meta/data/slapd-meta-rewrite.conf b/servers/slapd/back-meta/data/slapd-meta-rewrite.conf new file mode 100644 index 0000000000..e0ffb2ee37 --- /dev/null +++ b/servers/slapd/back-meta/data/slapd-meta-rewrite.conf @@ -0,0 +1,82 @@ +# +# master slapd config -- for testing of ldap metadirectory rewrite +# +ucdata-path ./ucdata +include ./schema/core.schema +include ./schema/cosine.schema +include ./schema/inetorgperson.schema +# +schemacheck off +# +pidfile ./meta-db/slapd.pid +argsfile ./meta-db/slapd.args + +access to attr=userPassword + by anonymous auth + by self write + +access to dn.regex="[^,]+,ou=People,dc=[^,]+,o=Foo Bar,c=US" attr=sn + by group.exact="cn=Users,ou=Groups,dc=bar,o=Foo Bar,c=US" read + by * none + +access to dn.regex="[^,]+,ou=Groups,dc=[^,]+,o=Foo Bar,c=US" attr=cn + by group.exact="cn=Users,ou=Groups,dc=bar,o=Foo Bar,c=US" read + by * none + +#access to dn.regex="[^,]+,ou=Groups,dc=[^,]+,o=Foo Bar,c=US" attr=cn +# by dnattr=member read +# by * none + +access to * + by * read + +####################################################################### +# ldbm database definitions +####################################################################### + +include ./slapd-ldbm.conf + +####################################################################### +# meta database definitions +####################################################################### + +database meta +suffix "o=Foo Bar, c=US" +dncache-ttl forever +lastmod off + +uri "ldap://localhost:@PORT@/ou=People, dc=foo, o=Foo Bar, c=US" +rewriteEngine on +rewriteContext default +rewriteRule "(.*)o=Foo Bar,[ ]?c=US" "%1dc=example, dc=com" +rewriteContext searchResult +rewriteRule "(.*)dc=example,[ ]?dc=com" "%1o=Foo Bar, c=US" +rewriteContext searchFilter +rewriteRule "(.*)member=([^)]+),o=Foo Bar,[ ]?c=US(.*)" "%1member=%2,dc=example,dc=com%3" + +uri "ldap://localhost:@PORT@/ou=People, dc=bar, o=Foo Bar, c=US" +rewriteEngine on +rewriteContext default +rewriteRule "(.*)o=Foo Bar,[ ]?c=US" "%1dc=example, dc=com" +rewriteContext searchResult +rewriteRule "(.*)dc=example,[ ]?dc=com" "%1o=Foo Bar, c=US" +rewriteContext searchFilter +rewriteRule "(.*)member=([^)]+),o=Foo Bar,[ ]?c=US(.*)" "%1member=%2,dc=example,dc=com%3" +default-target +map attribute givenName sn + +uri "ldap://localhost:@PORT@/ou=Groups, dc=bar, o=Foo Bar, c=US" +rewriteEngine on +rewriteContext default +rewriteRule "(.*)o=Foo Bar,[ ]?c=US" "%1dc=example, dc=com" +rewriteContext searchResult +rewriteRule "(.*)dc=example,[ ]?dc=com" "%1o=Foo Bar, c=US" +rewriteContext searchFilter +rewriteRule "(.*)member=([^)]+),o=Foo Bar,[ ]?c=US(.*)" "%1member=%2,dc=example,dc=com%3" + +####################################################################### +# ldap database with suffix massage definitions +####################################################################### + +include ./slapd-ldap.conf + diff --git a/servers/slapd/back-meta/delete.c b/servers/slapd/back-meta/delete.c new file mode 100644 index 0000000000..1acde0b198 --- /dev/null +++ b/servers/slapd/back-meta/delete.c @@ -0,0 +1,128 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +int +meta_back_delete( + Backend *be, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + struct metaconn *lc; + int candidate = -1; + + char *mdn = NULL; + + lc = meta_back_getconn( li, conn, op, META_OP_REQUIRE_SINGLE, + ndn, &candidate ); + if ( !lc || !meta_back_dobind( lc, op ) ) { + return -1; + } + + /* + * Rewrite the compare dn, if needed + */ + switch ( rewrite_session( li->targets[ candidate ]->rwinfo, + "deleteDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> deleteDn: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return( -1 ); + } + + ldap_delete_s( lc->conns[ candidate ]->ld, mdn ); + + if ( mdn != dn ) { + free( mdn ); + } + + return meta_back_op_result( lc, op ); +} + diff --git a/servers/slapd/back-meta/dncache.c b/servers/slapd/back-meta/dncache.c new file mode 100644 index 0000000000..febb963414 --- /dev/null +++ b/servers/slapd/back-meta/dncache.c @@ -0,0 +1,281 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +/* + * The dncache, at present, maps an entry to the target that holds it. + */ + +struct metadncacheentry { + char *dn; + int target; + + time_t lastupdated; +}; + +/* + * meta_dncache_cmp + * + * compares two struct metadncacheentry; used by avl stuff + * FIXME: modify avl stuff to delete an entry based on cmp + * (e.g. when ttl expired?) + */ +int +meta_dncache_cmp( + const void *c1, + const void *c2 +) +{ + struct metadncacheentry *cc1 = ( struct metadncacheentry * )c1; + struct metadncacheentry *cc2 = ( struct metadncacheentry * )c2; + + /* + * case sensitive, because the dn MUST be normalized + */ + return strcmp( cc1->dn, cc2->dn ); +} + +/* + * meta_dncache_dup + * + * returns -1 in case a duplicate struct metadncacheentry has been inserted; + * used by avl stuff + */ +int +meta_dncache_dup( + void *c1, + void *c2 +) +{ + struct metadncacheentry *cc1 = ( struct metadncacheentry * )c1; + struct metadncacheentry *cc2 = ( struct metadncacheentry * )c2; + + /* + * case sensitive, because the dn MUST be normalized + */ + return ( strcmp( cc1->dn, cc2->dn ) == 0 ) ? -1 : 0; +} + +/* + * meta_dncache_get_target + * + * returns the target a dn belongs to, or -1 in case the dn is not + * in the cache + */ +int +meta_dncache_get_target( + struct metadncache *cache, + const char *ndn +) +{ + struct metadncacheentry tmp_entry, *entry; + time_t curr_time; + int target = -1; + + tmp_entry.dn = ( char * )ndn; + ldap_pvt_thread_mutex_lock( &cache->mutex ); + entry = ( struct metadncacheentry * )avl_find( cache->tree, + ( caddr_t )&tmp_entry, meta_dncache_cmp ); + + if ( entry != NULL ) { + + /* + * if cache->ttl < 0, cache never expires; + * if cache->ttl = 0 no cache is used; shouldn't get here + * else, cache is used with ttl + */ + if ( cache->ttl < 0 ) { + target = entry->target; + } else { + + /* + * Need mutex? + */ + curr_time = time( NULL ); + + if ( entry->lastupdated+cache->ttl > curr_time ) { + target = entry->target; + } + } + } + ldap_pvt_thread_mutex_unlock( &cache->mutex ); + + return target; +} + +/* + * meta_dncache_update_entry + * + * updates target and lastupdated of a struct metadncacheentry if exists, + * otherwise it gets created; returns -1 in case of error + */ +int +meta_dncache_update_entry( + struct metadncache *cache, + const char *ndn, + int target +) +{ + struct metadncacheentry *entry, tmp_entry; + time_t curr_time = 0L; + int err = 0; + + /* + * if cache->ttl < 0, cache never expires; + * if cache->ttl = 0 no cache is used; shouldn't get here + * else, cache is used with ttl + */ + if ( cache->ttl > 0 ) { + + /* + * Need mutex? + */ + curr_time = time( NULL ); + } + + tmp_entry.dn = ( char * )ndn; + + ldap_pvt_thread_mutex_lock( &cache->mutex ); + entry = ( struct metadncacheentry * )avl_find( cache->tree, + ( caddr_t )&tmp_entry, meta_dncache_cmp ); + + if ( entry != NULL ) { + entry->target = target; + entry->lastupdated = curr_time; + } else { + entry = ch_calloc( sizeof( struct metadncacheentry ), 1 ); + if ( entry == NULL ) { + ldap_pvt_thread_mutex_unlock( &cache->mutex ); + return -1; + } + + entry->dn = ch_strdup( ndn ); + if ( entry->dn == NULL ) { + ldap_pvt_thread_mutex_unlock( &cache->mutex ); + return -1; + } + entry->target = target; + entry->lastupdated = curr_time; + + err = avl_insert( &cache->tree, ( caddr_t )entry, + meta_dncache_cmp, meta_dncache_dup ); + } + ldap_pvt_thread_mutex_unlock( &cache->mutex ); + + return err; +} + +/* + * meta_dncache_update_entry + * + * updates target and lastupdated of a struct metadncacheentry if exists, + * otherwise it gets created; returns -1 in case of error + */ +int +meta_dncache_delete_entry( + struct metadncache *cache, + const char *ndn +) +{ + struct metadncacheentry *entry, tmp_entry; + + tmp_entry.dn = ( char * )ndn; + + ldap_pvt_thread_mutex_lock( &cache->mutex ); + entry = avl_delete( &cache->tree, ( caddr_t )&tmp_entry, + meta_dncache_cmp ); + ldap_pvt_thread_mutex_lock( &cache->mutex ); + + if ( entry != NULL ) { + meta_dncache_free( ( void * )entry ); + } + + return 0; +} + +/* + * meta_dncache_free + * + * frees an entry + * + */ +void +meta_dncache_free( + void *e +) +{ + struct metadncacheentry *entry = ( struct metadncacheentry * )e; + + free( entry->dn ); +} + diff --git a/servers/slapd/back-meta/external.h b/servers/slapd/back-meta/external.h new file mode 100644 index 0000000000..814b22bd58 --- /dev/null +++ b/servers/slapd/back-meta/external.h @@ -0,0 +1,211 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#ifndef META_EXTERNAL_H +#define META_EXTERNAL_H + +LDAP_BEGIN_DECL + +extern int +meta_back_initialize LDAP_P(( + BackendInfo *bi +)); +extern int +meta_back_open LDAP_P(( + BackendInfo *bi +)); +extern int +meta_back_close LDAP_P(( + BackendInfo *bi +)); +extern int +meta_back_destroy LDAP_P(( + BackendInfo *bi +)); +extern int +meta_back_db_init LDAP_P(( + BackendDB *bd +)); +extern int +meta_back_db_destroy LDAP_P(( + BackendDB *bd +)); +extern int +meta_back_db_config LDAP_P(( + BackendDB *bd, + const char *fname, + int lineno, + int argc, + char **argv +)); +extern int +meta_back_bind LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn, + int method, + struct berval *cred, + char** edn +)); +extern int +meta_back_conn_destroy LDAP_P(( + BackendDB *bd, + Connection *conn +)); +extern int +meta_back_search LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + const char *base, + const char *nbase, + int scope, + int deref, + int sizelimit, + int timelimit, + Filter *filter, + const char *filterstr, + char **attrs, + int attrsonly +)); +extern int +meta_back_compare LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn, + AttributeAssertion *ava +)); +extern int +meta_back_modify LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn, + Modifications *ml +)); +extern int +meta_back_modrdn LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn, + const char *newrdn, + int deleteoldrdn, + const char *newSuperior +)); +extern int +meta_back_add LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + Entry *e +)); +extern int +meta_back_delete LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn +)); +extern int meta_back_abandon LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + int msgid +)); +extern int meta_back_group LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + Entry *target, + const char* gr_ndn, + const char* op_ndn, + ObjectClass* group_oc, + AttributeDescription* + group_at +)); +extern int +meta_back_attribute LDAP_P(( + BackendDB *bd, + Connection *conn, + Operation *op, + Entry *target, + const char* e_ndn, + AttributeDescription* entry_at, + struct berval ***vals +)); + +LDAP_END_DECL + +#endif /* META_EXTERNAL_H */ + diff --git a/servers/slapd/back-meta/group.c b/servers/slapd/back-meta/group.c new file mode 100644 index 0000000000..1fe9d6eff7 --- /dev/null +++ b/servers/slapd/back-meta/group.c @@ -0,0 +1,272 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + + +/* return 0 IFF op_dn is a value in group_at (member) attribute + * of entry with gr_dn AND that entry has an objectClass + * value of group_oc (groupOfNames) + */ +int +meta_back_group( + Backend *be, + Connection *conn, + Operation *op, + Entry *target, + const char *gr_ndn, + const char *op_ndn, + ObjectClass *group_oc, + AttributeDescription *group_at +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + int rc = 1, candidate; + Attribute *attr; + struct berval bv; + + AttributeDescription *ad_objectClass = slap_schema.si_ad_objectClass; + LDAPMessage *result; + char *gattr[ 2 ]; + char *filter; + LDAP *ld; + char *mop_ndn, *mgr_ndn; + + char *group_oc_name = NULL; + char *group_at_name = group_at->ad_cname->bv_val; + + if ( group_oc->soc_names && group_oc->soc_names[ 0 ] ) { + group_oc_name = group_oc->soc_names[ 0 ]; + } else { + group_oc_name = group_oc->soc_oid; + } + + if ( target != NULL && strcmp( target->e_ndn, gr_ndn ) == 0 ) { + /* we already have a copy of the entry */ + /* attribute and objectclass mapping has already been done */ + + /* + * first we need to check if the objectClass attribute + * has been retrieved; otherwise we need to repeat the search + */ + attr = attr_find( target->e_attrs, ad_objectClass ); + if ( attr != NULL ) { + + /* + * Now we can check for the group objectClass value + */ + if ( !is_entry_objectclass( target, group_oc ) ) { + return 1; + } + + /* + * This part has been reworked: the group attr compare + * fails only if the attribute is PRESENT but the value + * is NOT PRESENT; if the attribute is NOT PRESENT, the + * search must be repeated as well. + * This may happen if a search for an entry has already + * been performed (target is not null) but the group + * attribute has not been required + */ + attr = attr_find( target->e_attrs, group_at ); + if ( attr != NULL ) { + bv.bv_val = ( char * )op_ndn; + bv.bv_len = strlen( op_ndn ); + rc = value_find( group_at, attr->a_vals, &bv ); + if ( rc != LDAP_SUCCESS ) { + return 1; + } + return 0; + } /* else: repeat the search */ + } /* else: repeat the search */ + } /* else: do the search */ + + candidate = meta_back_select_unique_candidate( li, gr_ndn ); + if ( candidate == -1 ) { + return 1; + } + + /* + * Rewrite the op ndn if needed + */ + switch ( rewrite_session( li->targets[ candidate ]->rwinfo, "bindDn", + op_ndn, conn, &mop_ndn ) ) { + case REWRITE_REGEXEC_OK: + if ( mop_ndn == NULL ) { + mop_ndn = ( char * )op_ndn; + } + Debug( LDAP_DEBUG_ARGS, + "rw> bindDn (op ndn in group):" + " \"%s\" -> \"%s\"\n%s", + op_ndn, mop_ndn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + /* continues to next case */ + + case REWRITE_REGEXEC_ERR: + return 1; + } + + /* + * Rewrite the gr ndn if needed + */ + switch ( rewrite_session( li->targets[ candidate ]->rwinfo, + "searchBase", + gr_ndn, conn, &mgr_ndn ) ) { + case REWRITE_REGEXEC_OK: + if ( mgr_ndn == NULL ) { + mgr_ndn = ( char * )gr_ndn; + } + Debug( LDAP_DEBUG_ARGS, + "rw> searchBase (gr ndn in group):" + " \"%s\" -> \"%s\"\n%s", + gr_ndn, mgr_ndn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + /* continues to next case */ + + case REWRITE_REGEXEC_ERR: + return 1; + } + + group_oc_name = ldap_back_map( &li->targets[ candidate ]->oc_map, + group_oc_name, 0 ); + if ( group_oc_name == NULL ) { + return 1; + } + group_at_name = ldap_back_map( &li->targets[ candidate ]->at_map, + group_at_name, 0 ); + if ( group_at_name == NULL ) { + return 1; + } + + filter = ch_malloc( sizeof( "(&(objectclass=)(=))" ) + + strlen( group_oc_name ) + + strlen( group_at_name ) + + strlen( mop_ndn ) + 1 ); + if ( filter == NULL ) { + goto cleanup; + } + + rc = ldap_initialize( &ld, li->targets[ candidate ]->uri ); + if ( rc != LDAP_SUCCESS ) { + goto cleanup; + } + + rc = ldap_bind_s( ld, li->targets[ candidate ]->binddn, + li->targets[ candidate ]->bindpw, LDAP_AUTH_SIMPLE ); + if ( rc != LDAP_SUCCESS ) { + goto cleanup; + } + + strcpy( filter, "(&(objectclass=" ); + strcat( filter, group_oc_name ); + strcat( filter, ")(" ); + strcat( filter, group_at_name ); + strcat( filter, "=" ); + strcat( filter, mop_ndn ); + strcat( filter, "))" ); + + gattr[ 0 ] = "objectclass"; + gattr[ 1 ] = NULL; + rc = 1; + if ( ldap_search_ext_s( ld, mgr_ndn, LDAP_SCOPE_BASE, filter, + gattr, 0, NULL, NULL, LDAP_NO_LIMIT, + LDAP_NO_LIMIT, &result ) == LDAP_SUCCESS ) { + if ( ldap_first_entry( ld, result ) != NULL ) { + rc = 0; + } + ldap_msgfree( result ); + } + +cleanup: + if ( ld != NULL ) { + ldap_unbind( ld ); + } + if ( filter != NULL ) { + ch_free( filter ); + } + if ( mop_ndn != op_ndn ) { + free( mop_ndn ); + } + if ( mgr_ndn != gr_ndn ) { + free( mgr_ndn ); + } + + return rc; +} + diff --git a/servers/slapd/back-meta/init.c b/servers/slapd/back-meta/init.c new file mode 100644 index 0000000000..4e30347442 --- /dev/null +++ b/servers/slapd/back-meta/init.c @@ -0,0 +1,250 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +#ifdef SLAPD_META_DYNAMIC + +int +back_meta_LTX_init_module( int argc, char *argv[] ) { + BackendInfo bi; + + memset( &bi, '\0', sizeof( bi ) ); + bi.bi_type = "meta"; + bi.bi_init = meta_back_initialize; + + backend_add( &bi ); + return 0; +} + +#endif /* SLAPD_META_DYNAMIC */ + +int +meta_back_initialize( + BackendInfo *bi +) +{ + bi->bi_open = 0; + bi->bi_config = 0; + bi->bi_close = 0; + bi->bi_destroy = 0; + + bi->bi_db_init = meta_back_db_init; + bi->bi_db_config = meta_back_db_config; + bi->bi_db_open = 0; + bi->bi_db_close = 0; + bi->bi_db_destroy = meta_back_db_destroy; + + bi->bi_op_bind = meta_back_bind; + bi->bi_op_unbind = 0; + bi->bi_op_search = meta_back_search; + bi->bi_op_compare = meta_back_compare; + bi->bi_op_modify = meta_back_modify; + bi->bi_op_modrdn = meta_back_modrdn; + bi->bi_op_add = meta_back_add; + bi->bi_op_delete = meta_back_delete; + bi->bi_op_abandon = 0; + + bi->bi_extended = 0; + + bi->bi_acl_group = meta_back_group; + bi->bi_acl_attribute = meta_back_attribute; + bi->bi_chk_referrals = 0; + + bi->bi_connection_init = 0; + bi->bi_connection_destroy = meta_back_conn_destroy; + + return 0; +} + +int +meta_back_db_init( + Backend *be +) +{ + struct metainfo *li; + + li = ch_calloc( 1, sizeof( struct metainfo ) ); + if ( li == NULL ) { + return -1; + } + + /* + * At present the default is no default target; + * this may change + */ + li->defaulttarget = META_DEFAULT_TARGET_NONE; + + ldap_pvt_thread_mutex_init( &li->conn_mutex ); + ldap_pvt_thread_mutex_init( &li->cache.mutex ); + be->be_private = li; + + return 0; +} + +static void +conn_free( + struct metaconn *lc +) +{ + struct metasingleconn **lsc; + + for ( lsc = lc->conns; lsc[ 0 ] != NULL; lsc++ ) { + if ( lsc[ 0 ]->ld != NULL ) { + ldap_unbind( lsc[ 0 ]->ld ); + } + if ( lsc[ 0 ]->bound_dn ) { + free( lsc[ 0 ]->bound_dn ); + } + free( lsc[ 0 ] ); + } + free( lc->conns ); + free( lc ); +} + +static void +target_free( + struct metatarget *lt +) +{ + if ( lt->uri ) { + free( lt->uri ); + } + if ( lt->binddn ) { + free( lt->binddn ); + } + if ( lt->bindpw ) { + free( lt->bindpw ); + } + if ( lt->rwinfo ) { + rewrite_info_delete( lt->rwinfo ); + } + avl_free( lt->oc_map.remap, NULL ); + avl_free( lt->oc_map.map, ( AVL_FREE )mapping_free ); + avl_free( lt->at_map.remap, NULL ); + avl_free( lt->at_map.map, ( AVL_FREE )mapping_free ); +} + +int +meta_back_db_destroy( + Backend *be +) +{ + struct metainfo *li; + + if ( be->be_private ) { + int i; + + li = ( struct metainfo * )be->be_private; + + /* + * Destroy the connection tree + */ + ldap_pvt_thread_mutex_lock( &li->conn_mutex ); + + if ( li->conntree ) { + avl_free( li->conntree, + ( AVL_FREE )conn_free ); + } + + ldap_pvt_thread_mutex_unlock( &li->cache.mutex ); + ldap_pvt_thread_mutex_destroy( &li->cache.mutex ); + + /* + * Destroy the per-target stuff (assuming there's at + * least one ...) + */ + for ( i = 0; i < li->ntargets; i++ ) { + target_free( li->targets[ i ] ); + free( li->targets[ i ] ); + } + + free( li->targets ); + + ldap_pvt_thread_mutex_lock( &li->cache.mutex ); + if ( li->cache.tree ) { + avl_free( li->cache.tree, + ( AVL_FREE )meta_dncache_free ); + } + + ldap_pvt_thread_mutex_unlock( &li->cache.mutex ); + ldap_pvt_thread_mutex_destroy( &li->cache.mutex ); + + + } + + free( be->be_private ); + return 0; +} + diff --git a/servers/slapd/back-meta/modify.c b/servers/slapd/back-meta/modify.c new file mode 100644 index 0000000000..10cda0b78f --- /dev/null +++ b/servers/slapd/back-meta/modify.c @@ -0,0 +1,195 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +int +meta_back_modify( + Backend *be, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn, + Modifications *modlist +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + struct metaconn *lc; + LDAPMod **modv; + LDAPMod *mods; + Modifications *ml; + int candidate = -1, i; + char *mdn, *mapped; + + lc = meta_back_getconn( li, conn, op, META_OP_REQUIRE_SINGLE, + ndn, &candidate ); + if ( !lc || !meta_back_dobind( lc, op ) ) { + return -1; + } + + /* + * Rewrite the modify dn, if needed + */ + switch ( rewrite_session( li->targets[ candidate ]->rwinfo, + "modifyDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> modifyDN: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return( -1 ); + } + + for ( i = 0, ml = modlist; ml; i++ ,ml = ml->sml_next ) + ; + + mods = ch_malloc( sizeof( LDAPMod )*i ); + if ( mods == NULL ) { + if ( mdn != dn ) { + free( mdn ); + } + return -1; + } + modv = ( LDAPMod ** )ch_malloc( ( i + 1 )*sizeof( LDAPMod * ) ); + if ( modv == NULL ) { + free( mods ); + if ( mdn != dn ) { + free( mdn ); + } + return -1; + } + + for ( i = 0, ml = modlist; ml; ml = ml->sml_next ) { + /* + * lastmod should always be <off> + */ +#if 0 + if ( !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_creatorsName->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_createTimestamp->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_modifiersName->ad_cname->bv_val ) + || !strcasecmp( a->a_desc->ad_cname->bv_val, + slap_schema.si_ad_modifyTimestamp->ad_cname->bv_val ) ) { + continue; + } +#endif + + mapped = ldap_back_map( &li->targets[ candidate ]->at_map, + ml->sml_desc->ad_cname->bv_val, 0 ); + if ( mapped == NULL ) { + continue; + } + + modv[ i ] = &mods[ i ]; + mods[ i ].mod_op = ml->sml_op | LDAP_MOD_BVALUES; + mods[ i ].mod_type = mapped; + + /* + * FIXME: dn-valued attrs should be rewritten + * to allow their use in ACLs at the back-ldap + * level. + */ + if ( strcmp( ml->sml_desc->ad_type->sat_syntax->ssyn_oid, + SLAPD_DN_SYNTAX ) == 0 ) { + ldap_dnattr_rewrite( + li->targets[ candidate ]->rwinfo, + ml->sml_bvalues, conn ); + } + + mods[ i ].mod_bvalues = ml->sml_bvalues; + i++; + } + modv[ i ] = 0; + + ldap_modify_s( lc->conns[ candidate ]->ld, mdn, modv ); + + if ( mdn != dn ) { + free( mdn ); + } + free( mods ); + free( modv ); + return meta_back_op_result( lc, op ); +} + diff --git a/servers/slapd/back-meta/modrdn.c b/servers/slapd/back-meta/modrdn.c new file mode 100644 index 0000000000..c4929f2f4a --- /dev/null +++ b/servers/slapd/back-meta/modrdn.c @@ -0,0 +1,187 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +int +meta_back_modrdn( + Backend *be, + Connection *conn, + Operation *op, + const char *dn, + const char *ndn, + const char *newrdn, + int deleteoldrdn, + const char *newSuperior +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + struct metaconn *lc; + int candidate = -1; + + char *mdn = NULL, *mnewSuperior = NULL; + + lc = meta_back_getconn( li, conn, op, META_OP_REQUIRE_SINGLE, + ndn, &candidate ); + if ( !lc || !meta_back_dobind( lc, op ) ) { + return -1; + } + + if ( newSuperior ) { + int nsCandidate, version = LDAP_VERSION3; + + nsCandidate = meta_back_select_unique_candidate( li, + newSuperior ); + + if ( nsCandidate != candidate ) { + /* + * FIXME: one possibility is to delete the entry + * from one target and add it to the other; + * unfortunately we'd need write access to both, + * which is nearly impossible; for administration + * needs, the rootdn of the metadirectory could + * be mapped to an administrative account on each + * target (the binddn?); we'll see. + */ + /* + * FIXME: is this the correct return code? + */ + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + return -1; + } + + ldap_set_option( lc->conns[ nsCandidate ]->ld, + LDAP_OPT_PROTOCOL_VERSION, &version ); + + /* + * Rewrite the new superior, if defined and required + */ + switch ( rewrite_session( li->targets[ nsCandidate ]->rwinfo, + "newSuperiorDn", + newSuperior, conn, &mnewSuperior ) ) { + case REWRITE_REGEXEC_OK: + if ( mnewSuperior == NULL ) { + mnewSuperior = ( char * )newSuperior; + } + Debug( LDAP_DEBUG_ARGS, "rw> newSuperiorDn:" + " \"%s\" -> \"%s\"\n%s", + newSuperior, mnewSuperior, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return( -1 ); + } + } + + /* + * Rewrite the modrdn dn, if required + */ + switch ( rewrite_session( li->targets[ candidate ]->rwinfo, + "modrDn", dn, conn, &mdn ) ) { + case REWRITE_REGEXEC_OK: + if ( mdn == NULL ) { + mdn = ( char * )dn; + } + Debug( LDAP_DEBUG_ARGS, "rw> modrDn: \"%s\" -> \"%s\"\n%s", + dn, mdn, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", NULL, NULL ); + + case REWRITE_REGEXEC_ERR: + return( -1 ); + } + + ldap_rename2_s( lc->conns[ candidate ]->ld, mdn, newrdn, + mnewSuperior, deleteoldrdn ); + + if ( mdn != dn ) { + free( mdn ); + } + if ( mnewSuperior != NULL && mnewSuperior != newSuperior ) { + free( mnewSuperior ); + } + + return meta_back_op_result( lc, op ); +} diff --git a/servers/slapd/back-meta/search.c b/servers/slapd/back-meta/search.c new file mode 100644 index 0000000000..5286128cce --- /dev/null +++ b/servers/slapd/back-meta/search.c @@ -0,0 +1,666 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" +#include "ldap_pvt.h" + +static void +meta_send_entry( + Backend *be, + Operation *op, + struct metaconn *lc, + int i, + LDAPMessage *e, + char **attrs, + int attrsonly +); + +static int +is_one_level_rdn( + const char *rdn, + int len +); + +int +meta_back_search( + Backend *be, + Connection *conn, + Operation *op, + const char *base, + const char *nbase, + int scope, + int deref, + int size, + int time, + Filter *filter, + const char *filterstr, + char **attrs, + int attrsonly +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + struct metaconn *lc; + struct metasingleconn **lsc; + struct timeval tv; + LDAPMessage *res, *e; + int count, rc = 0, *msgid, sres = LDAP_NO_SUCH_OBJECT; + char *match = NULL, *err = NULL; + char *mbase = NULL, *mfilter = NULL, *mmatch = NULL, + *mapped_filter = NULL, **mapped_attrs = NULL; + + int i, last = 0, candidates = 0, nbaselen, op_type; + + if ( scope == LDAP_SCOPE_BASE ) { + op_type = META_OP_REQUIRE_SINGLE; + } else { + op_type = META_OP_ALLOW_MULTIPLE; + } + + lc = meta_back_getconn( li, conn, op, op_type, nbase, NULL ); + if ( !lc || !meta_back_dobind( lc, op ) ) { + return -1; + } + + /* + * Array of message id of each target + */ + msgid = ch_calloc( sizeof( int ), li->ntargets ); + if ( msgid == NULL ) { + return -1; + } + + nbaselen = strlen( nbase ); + + /* + * Inits searches + */ + for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) { + char *realbase = ( char * )base; + int realscope = scope; + int suffixlen; + + if ( lsc[ 0 ]->candidate != META_CANDIDATE ) { + continue; + } + + if ( deref != -1 ) { + ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_DEREF, + ( void * )&deref); + } + if ( time != -1 ) { + ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_TIMELIMIT, + ( void * )&time); + } + if ( size != -1 ) { + ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_SIZELIMIT, + ( void * )&size); + } + + /* + * modifies the base according to the scope, if required + */ + suffixlen = strlen( li->targets[ i ]->suffix ); + if ( suffixlen > nbaselen ) { + switch ( scope ) { + case LDAP_SCOPE_SUBTREE: + /* + * make the target suffix the new base + */ + realbase = li->targets[ i ]->suffix; + break; + + case LDAP_SCOPE_ONELEVEL: + if ( is_one_level_rdn( li->targets[ i ]->suffix, + suffixlen-nbaselen-1) ) { + /* + * if there is exactly one level, + * make the target suffix the new + * base, and make scope "base" + */ + realbase = li->targets[ i ]->suffix; + realscope = LDAP_SCOPE_BASE; + break; + } /* else continue with the next case */ + + case LDAP_SCOPE_BASE: + /* + * this target is no longer candidate + */ + lsc[ 0 ]->candidate = META_NOT_CANDIDATE; + continue; + // rc = meta_back_op_result(lc, op); + // goto finish; + } + + } + + /* + * Rewrite the search base, if required + */ + switch ( rewrite_session( li->targets[ i ]->rwinfo, + "searchBase", + realbase, conn, &mbase ) ) { + case REWRITE_REGEXEC_OK: + if ( mbase == NULL ) { + mbase = realbase; + } + Debug( LDAP_DEBUG_ARGS, "rw> searchBase: \"%s\" -> \"%s\"\n%s", + base, mbase, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + /* continue to the next case */ + + case REWRITE_REGEXEC_ERR: + rc = -1; + goto finish; + } + + /* + * Rewrite the search filter, if required + */ + switch ( rewrite_session( li->targets[ i ]->rwinfo, + "searchFilter", + filterstr, conn, &mfilter ) ) { + case REWRITE_REGEXEC_OK: + if ( mfilter == NULL || mfilter[ 0 ] == '\0') { + if ( mfilter != NULL ) { + free( mfilter ); + } + mfilter = ( char * )filterstr; + } + Debug( LDAP_DEBUG_ARGS, + "rw> searchFilter: \"%s\" -> \"%s\"\n%s", + filterstr, mfilter, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + /* continue to the next case */ + + case REWRITE_REGEXEC_ERR: + rc = -1; + goto finish; + } + + /* + * Maps attributes in filter + */ + mapped_filter = ldap_back_map_filter( &li->targets[ i ]->at_map, + &li->targets[ i ]->oc_map, + ( char * )mfilter, 0 ); + if ( mapped_filter == NULL ) { + mapped_filter = ( char * )mfilter; + } + + /* + * Maps required attributes + */ + mapped_attrs = ldap_back_map_attrs( &li->targets[ i ]->at_map, + attrs, 0 ); + if ( mapped_attrs == NULL ) { + mapped_attrs = attrs; + } + + /* + * Starts the search + */ + msgid[ i ] = ldap_search( lsc[ 0 ]->ld, mbase, realscope, + mapped_filter, mapped_attrs, attrsonly); + if ( msgid[ i ] == -1 ) { + lsc[ 0 ]->candidate = META_NOT_CANDIDATE; + continue; + } + + if ( mapped_attrs != attrs ) { + charray_free( mapped_attrs ); + mapped_attrs = NULL; + } + if ( mapped_filter != mfilter ) { + free( mapped_filter ); + mapped_filter = NULL; + } + if ( mfilter != filterstr ) { + free( mfilter ); + mfilter = NULL; + } + if ( mbase != realbase ) { + free( mbase ); + mbase = NULL; + } + + ++candidates; + } + + /* We pull apart the ber result, stuff it into a slapd entry, and + * let send_search_entry stuff it back into ber format. Slow & ugly, + * but this is necessary for version matching, and for ACL processing. + */ + + + /* + * In case there are no candidates, no cycle takes place... + */ + for ( count = 0, rc = 0; candidates > 0; ) { + int ab, gotit = 0; + + /* check for abandon */ + ldap_pvt_thread_mutex_lock( &op->o_abandonmutex ); + ab = op->o_abandon; + ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex ); + + for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; lsc++, i++ ) { + if ( lsc[ 0 ]->candidate != META_CANDIDATE ) { + continue; + } + + if ( ab ) { + ldap_abandon( lsc[ 0 ]->ld, msgid[ i ] ); + rc = 0; + break; + } + + rc = ldap_result( lsc[ 0 ]->ld, LDAP_RES_ANY, + 0, &tv, &res ); + + if ( rc == 0 ) { + continue; + } else if ( rc == -1 ) { + /* something REALLY bad happened! */ + ( void )meta_clear_unused_candidates( li, + lc, -1, 0 ); + send_search_result( conn, op, + LDAP_OPERATIONS_ERROR, + "", "", NULL, NULL, count ); + + /* anything else needs be done? */ + goto finish; + } else if ( rc == LDAP_RES_SEARCH_ENTRY ) { + e = ldap_first_entry( lsc[ 0 ]->ld,res ); + meta_send_entry(be, op, lc, i, e, attrs, + attrsonly); + count++; + ldap_msgfree( res ); + gotit = 1; + } else { + sres = ldap_result2error( lsc[ 0 ]->ld, + res, 1 ); + sres = ldap_back_map_result( sres ); + if ( err != NULL ) { + free( err ); + } + ldap_get_option( lsc[ 0 ]->ld, + LDAP_OPT_ERROR_STRING, &err ); + if ( match != NULL ) { + free( match ); + } + ldap_get_option( lsc[ 0 ]->ld, + LDAP_OPT_MATCHED_DN, &match ); + + Debug( LDAP_DEBUG_ARGS, + "meta_back_search=> [%d] match=\"%s\" err=\"%s\"\n", + i, match, err ); + + last = i; + rc = 0; + + /* + * When no candidates are left, + * the outer cycle finishes + */ + lsc[ 0 ]->candidate = META_NOT_CANDIDATE; + --candidates; + } + } + + if ( ab ) { + goto finish; + } + + if ( gotit == 0 ) { + tv.tv_sec = 0; + tv.tv_usec = 100000; + ldap_pvt_thread_yield(); + } else { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + + if ( rc == -1 ) { + /* + * FIXME: need a strategy to handle errors + */ + rc = meta_back_op_result( lc, op ); + goto finish; + } + + /* + * Rewrite the matched portion of the search base, if required + * + * FIXME: only the last one gets caught! + */ + if ( match != NULL ) { + switch ( rewrite_session( li->targets[ last ]->rwinfo, + "matchedDn", match, conn, &mmatch ) ) { + case REWRITE_REGEXEC_OK: + if ( mmatch == NULL ) { + mmatch = ( char * )match; + } + Debug( LDAP_DEBUG_ARGS, "rw> matchedDn:" + " \"%s\" -> \"%s\"\n%s", + match, mmatch, "" ); + break; + + case REWRITE_REGEXEC_UNWILLING: + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + NULL, "Unwilling to perform", + NULL, NULL ); + /* continue to the next case */ + + case REWRITE_REGEXEC_ERR: + rc = -1; + goto finish; + } + } + + send_search_result( conn, op, sres, + mmatch, err, NULL, NULL, count ); + +finish: + if ( match ) { + if ( mmatch != match ) { + free( mmatch ); + } + free(match); + } + + if ( err ) { + free( err ); + } + + if ( msgid ) { + free( msgid ); + } + + return rc; +} + +static void +meta_send_entry( + Backend *be, + Operation *op, + struct metaconn *lc, + int target, + LDAPMessage *e, + char **attrs, + int attrsonly +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + char *a, *mapped; + Entry ent; + BerElement *ber = NULL; + Attribute *attr, **attrp; + struct berval *dummy = NULL; + struct berval *bv; + const char *text; + char *dn; + + struct metasingleconn *lsc = lc->conns[ target ]; + + dn = ldap_get_dn( lsc->ld, e ); + if ( dn == NULL ) { + return; + } + + /* + * Rewrite the dn of the result, if needed + */ + switch ( rewrite_session( li->targets[ target ]->rwinfo, + "searchResult", dn, lc->conn, &ent.e_dn ) ) { + case REWRITE_REGEXEC_OK: + if ( ent.e_dn == NULL ) { + ent.e_dn = dn; + } else { + Debug( LDAP_DEBUG_ARGS, "rw> searchResult[%d]: \"%s\"" + " -> \"%s\"\n", target, dn, ent.e_dn ); + free( dn ); + dn = NULL; + } + break; + + case REWRITE_REGEXEC_ERR: + case REWRITE_REGEXEC_UNWILLING: + free( dn ); + return; + } + + ent.e_ndn = ch_strdup( ent.e_dn ); + ( void )dn_normalize( ent.e_ndn ); + + /* + * cache dn + */ + if ( li->cache.ttl != META_DNCACHE_DISABLED ) { + ( void )meta_dncache_update_entry( &li->cache, + ch_strdup( ent.e_ndn ), + target ); + } + + ent.e_id = 0; + ent.e_attrs = 0; + ent.e_private = 0; + attrp = &ent.e_attrs; + + for ( a = ldap_first_attribute( lsc->ld, e, &ber ); + a != NULL; + a = ldap_next_attribute( lsc->ld, e, ber ) ) + { + mapped = ldap_back_map( &li->targets[ target ]->at_map, a, 1 ); + if ( mapped == NULL ) { + continue; + } + attr = ( Attribute * )ch_malloc( sizeof( Attribute ) ); + if ( attr == NULL ) { + continue; + } + attr->a_next = 0; + attr->a_desc = NULL; + if ( slap_str2ad( mapped, &attr->a_desc, &text ) + != LDAP_SUCCESS) { + ch_free( attr ); + continue; + } + attr->a_vals = ldap_get_values_len( lsc->ld, e, a ); + if ( !attr->a_vals ) { + attr->a_vals = &dummy; + } else if ( strcasecmp( mapped, "objectClass" ) == 0 ) { + int i, last; + for ( last = 0; attr->a_vals[ last ]; ++last ) ; + for ( i = 0; ( bv = attr->a_vals[ i ] ); i++ ) { + mapped = ldap_back_map( + &li->targets[ target]->oc_map, + bv->bv_val, 1 ); + if ( mapped == NULL ) { + ber_bvfree( attr->a_vals[ i ] ); + attr->a_vals[ i ] = NULL; + if ( --last < 0 ) { + break; + } + attr->a_vals[ i ] = + attr->a_vals[ last ]; + attr->a_vals[ last ] = NULL; + --i; + } else if ( mapped != bv->bv_val ) { + ch_free( bv->bv_val ); + bv->bv_val = ch_strdup( mapped ); + bv->bv_len = strlen( mapped ); + } + } + /* + * It is necessary to try to rewrite attributes with + * dn syntax because they might be used in ACLs as + * members of groups; since ACLs are applied to the + * rewritten stuff, no dn-based subecj clause could + * be used at the ldap backend side (see + * http://www.OpenLDAP.org/faq/data/cache/452.html) + * The problem can be overcome by moving the dn-based + * ACLs to the target directory server, and letting + * everything pass thru the ldap backend. + */ + } else if ( strcmp( attr->a_desc->ad_type->sat_syntax->ssyn_oid, + SLAPD_DN_SYNTAX ) == 0 ) { + int i; + for ( i = 0; ( bv = attr->a_vals[ i ] ); i++ ) { + char *newval; + + switch ( rewrite_session( li->targets[ target ]->rwinfo, + "searchResult", bv->bv_val, + lc->conn, &newval )) { + case REWRITE_REGEXEC_OK: + /* left as is */ + if ( newval == NULL ) { + break; + } + Debug( LDAP_DEBUG_ARGS, + "rw> searchResult on attr=%s: \"%s\" -> \"%s\"\n", + attr->a_desc->ad_type->sat_cname, + bv->bv_val, newval ); + + free( bv->bv_val ); + bv->bv_val = newval; + bv->bv_len = strlen( newval ); + + break; + + case REWRITE_REGEXEC_UNWILLING: + + case REWRITE_REGEXEC_ERR: + /* + * FIXME: better give up, + * skip the attribute + * or leave it untouched? + */ + break; + } + } + } + *attrp = attr; + attrp = &attr->a_next; + } + send_search_entry( be, lc->conn, op, &ent, attrs, attrsonly, NULL ); + while ( ent.e_attrs ) { + attr = ent.e_attrs; + ent.e_attrs = attr->a_next; + ad_free( attr->a_desc, 1 ); + if ( attr->a_vals != &dummy ) { + ber_bvecfree(attr->a_vals); + } + free( attr ); + } + if ( ber ) { + ber_free( ber, 0 ); + } + + if ( ent.e_dn ) { + free( ent.e_dn ); + } + if ( ent.e_ndn ) { + free( ent.e_ndn ); + } +} + +static int +is_one_level_rdn( + const char *rdn, + int len +) +{ + for ( ; len--; ) { + if ( LDAP_DNSEPARATOR( rdn[ len ] ) ) { + return 0; + } + } + + return 1; +} + diff --git a/servers/slapd/back-meta/unbind.c b/servers/slapd/back-meta/unbind.c new file mode 100644 index 0000000000..71086e58a4 --- /dev/null +++ b/servers/slapd/back-meta/unbind.c @@ -0,0 +1,127 @@ +/* + * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + * + * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it> + * + * This work has been developed to fulfill the requirements + * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated + * to the OpenLDAP Foundation in the hope that it may be useful + * to the Open Source community, but WITHOUT ANY WARRANTY. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author and SysNet s.n.c. are not responsible for the consequences + * of use of this software, no matter how awful, even if they arise from + * flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * SysNet s.n.c. cannot be responsible for the consequences of the + * alterations. + * + * 4. This notice may not be removed or altered. + * + * + * This software is based on the backend back-ldap, implemented + * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence + * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other + * contributors. The contribution of the original software to the present + * implementation is acknowledged in this copyright statement. + * + * A special acknowledgement goes to Howard for the overall architecture + * (and for borrowing large pieces of code), and to Mark, who implemented + * from scratch the attribute/objectclass mapping. + * + * The original copyright statement follows. + * + * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com> + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the + * documentation. + * + * 4. This notice may not be removed or altered. + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-meta.h" + +int +meta_back_conn_destroy( + Backend *be, + Connection *conn +) +{ + struct metainfo *li = ( struct metainfo * )be->be_private; + struct metaconn *lc, lc_curr; + + Debug( LDAP_DEBUG_TRACE, + "=>meta_back_conn_destroy: fetching conn %ld\n%s%s", + conn->c_connid, "", "" ); + + lc_curr.conn = conn; + + ldap_pvt_thread_mutex_lock( &li->conn_mutex ); + lc = avl_delete( &li->conntree, ( caddr_t )&lc_curr, + meta_back_conn_cmp ); + ldap_pvt_thread_mutex_unlock( &li->conn_mutex ); + + if ( lc ) { + int i; + + Debug( LDAP_DEBUG_TRACE, + "=>meta_back_conn_destroy: destroying conn %ld\n%s%s", + lc->conn->c_connid, "", "" ); + + /* + * Cleanup rewrite session + */ + for ( i = 0; i < li->ntargets; ++i ) { + if ( lc->conns[ i ]->ld == NULL ) { + free( lc->conns[ i ] ); + continue; + } + + rewrite_session_delete( li->targets[ i ]->rwinfo, conn ); + meta_clear_one_candidate( lc->conns[ i ], 1 ); + free( lc->conns[ i ] ); + } + + free( lc->conns ); + free( lc ); + } + + /* no response to unbind */ + + return 0; +} + diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c index 19802a61d5..1b26ee117f 100644 --- a/servers/slapd/backend.c +++ b/servers/slapd/backend.c @@ -30,6 +30,9 @@ #ifdef SLAPD_LDBM #include "back-ldbm/external.h" #endif +#ifdef SLAPD_META +#include "back-meta/external.h" +#endif #ifdef SLAPD_PASSWD #include "back-passwd/external.h" #endif @@ -62,6 +65,9 @@ static BackendInfo binfo[] = { #if defined(SLAPD_LDBM) && !defined(SLAPD_LDBM_DYNAMIC) {"ldbm", ldbm_back_initialize}, #endif +#if defined(SLAPD_META) && !defined(SLAPD_META_DYNAMIC) + {"meta", meta_back_initialize}, +#endif #if defined(SLAPD_PASSWD) && !defined(SLAPD_PASSWD_DYNAMIC) {"passwd", passwd_back_initialize}, #endif -- 2.39.5