summaryrefslogtreecommitdiffstats
path: root/libraries/librewrite
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libraries/librewrite/Copyright23
-rw-r--r--libraries/librewrite/Makefile.in37
-rw-r--r--libraries/librewrite/RATIONALE2
-rw-r--r--libraries/librewrite/config.c441
-rw-r--r--libraries/librewrite/context.c474
-rw-r--r--libraries/librewrite/info.c284
-rw-r--r--libraries/librewrite/ldapmap.c454
-rw-r--r--libraries/librewrite/map.c582
-rw-r--r--libraries/librewrite/params.c147
-rw-r--r--libraries/librewrite/parse.c124
-rw-r--r--libraries/librewrite/rewrite-int.h628
-rw-r--r--libraries/librewrite/rewrite-map.h32
-rw-r--r--libraries/librewrite/rewrite.c195
-rw-r--r--libraries/librewrite/rule.c510
-rw-r--r--libraries/librewrite/session.c427
-rw-r--r--libraries/librewrite/subst.c513
-rw-r--r--libraries/librewrite/var.c273
-rw-r--r--libraries/librewrite/xmap.c506
18 files changed, 5652 insertions, 0 deletions
diff --git a/libraries/librewrite/Copyright b/libraries/librewrite/Copyright
new file mode 100644
index 0000000..64a25f5
--- /dev/null
+++ b/libraries/librewrite/Copyright
@@ -0,0 +1,23 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * 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 0000000..9e8dc3f
--- /dev/null
+++ b/libraries/librewrite/Makefile.in
@@ -0,0 +1,37 @@
+# LIBREWRITE
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## Copyright 2000-2001 Pierangelo Masarati <ando@sys-net.it>
+##
+
+SRCS = config.c context.c info.c ldapmap.c map.c params.c rule.c \
+ session.c subst.c var.c xmap.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 xmap.o
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+LIBRARY = librewrite.a
+PROGRAMS = rewrite
+XLIBS = $(LIBRARY) $(LDAP_LIBLUTIL_A) \
+ $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+rewrite: $(XLIBS) 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 0000000..c8fa386
--- /dev/null
+++ b/libraries/librewrite/RATIONALE
@@ -0,0 +1,2 @@
+The workings of the rewrite library are described in the
+REWRITING section of the slapd-meta(5) manual page.
diff --git a/libraries/librewrite/config.c b/libraries/librewrite/config.c
new file mode 100644
index 0000000..132c84e
--- /dev/null
+++ b/libraries/librewrite/config.c
@@ -0,0 +1,441 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+/*
+ * 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 [numPassesPerRule]
+ * 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",
+ fname, lineno );
+ return -1;
+
+ } else if ( argc > 2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] extra fields in rewriteEngine"
+ " will be discarded\n",
+ 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",
+ 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",
+ fname, lineno );
+ return -1;
+ }
+
+ if ( lutil_atoi( &info->li_max_passes, argv[ 1 ] ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unable to parse rewriteMaxPasses=\"%s\"\n",
+ fname, lineno, argv[ 1 ] );
+ return -1;
+ }
+
+ if ( info->li_max_passes <= 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] negative or null rewriteMaxPasses\n",
+ fname, lineno );
+ return -1;
+ }
+
+ if ( argc > 2 ) {
+ if ( lutil_atoi( &info->li_max_passes_per_rule, argv[ 2 ] ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unable to parse rewriteMaxPassesPerRule=\"%s\"\n",
+ fname, lineno, argv[ 2 ] );
+ return -1;
+ }
+
+ if ( info->li_max_passes_per_rule <= 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] negative or null rewriteMaxPassesPerRule\n",
+ fname, lineno );
+ return -1;
+ }
+
+ } else {
+ info->li_max_passes_per_rule = info->li_max_passes;
+ }
+ 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",
+ fname, lineno );
+ return -1;
+ }
+
+ /*
+ * Checks for existence (lots of contexts should be
+ * available by default ...)
+ */
+ rewrite_int_curr_context = rewrite_context_find( info, argv[ 1 ] );
+ if ( rewrite_int_curr_context == NULL ) {
+ rewrite_int_curr_context = rewrite_context_create( info,
+ argv[ 1 ] );
+ }
+ if ( rewrite_int_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",
+ 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",
+ 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;
+ }
+
+ rewrite_int_curr_context->lc_alias = aliased;
+ rewrite_int_curr_context = aliased;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] extra fields"
+ " in rewriteContext"
+ " will be discarded\n",
+ 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",
+ fname, lineno );
+ return -1;
+
+ } else if ( argc > 4 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] extra fields in rewriteRule"
+ " will be discarded\n",
+ fname, lineno );
+ }
+
+ if ( rewrite_int_curr_context == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteRule outside a"
+ " context; will add to default\n",
+ fname, lineno );
+ rewrite_int_curr_context = rewrite_context_find( info,
+ REWRITE_DEFAULT_CONTEXT );
+
+ /*
+ * Default context MUST exist in a properly initialized
+ * struct rewrite_info
+ */
+ assert( rewrite_int_curr_context != NULL );
+ }
+
+ rc = rewrite_rule_compile( info, rewrite_int_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",
+ 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",
+ 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, argv[ 0 ] );
+ 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 = ( const struct rewrite_builtin_map * )c1;
+ m2 = ( const 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 ldap_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 * )ldap_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 (( map->lb_mapper = rewrite_mapper_find( argv[ MAP_TYPE ] ))) {
+ map->lb_type = REWRITE_BUILTIN_MAP;
+
+#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->lb_mapper->rm_config( fname, lineno,
+ argc - 3, argv + 3 );
+
+ /*
+ * Error
+ */
+ } else {
+ free( map );
+ Debug( LDAP_DEBUG_ANY, "[%s:%d] unknown map type\n",
+ 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 0000000..78ad6c9
--- /dev/null
+++ b/libraries/librewrite/context.c
@@ -0,0 +1,474 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#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
+)
+{
+ const struct rewrite_context *lc1, *lc2;
+
+ lc1 = (const struct rewrite_context *)c1;
+ lc2 = (const 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 *)ldap_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;
+ }
+ memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) );
+
+ /*
+ * Add context to tree
+ */
+ rc = ldap_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++;
+
+ Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
+ " [depth=%d] string='%s'\n",
+ op->lo_depth, string );
+ assert( op->lo_depth > 0 );
+
+ s = (char *)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" );
+
+ /*
+ * 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" );
+ 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 successful rewriting
+ */
+ case REWRITE_REGEXEC_OK:
+
+ /*
+ * It matched! Check for actions ...
+ */
+ if ( res != NULL ) {
+ struct rewrite_action *action;
+
+ if ( s != string && s != res ) {
+ 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;
+
+ /*
+ * This ends the rewrite context
+ * and returns a user-defined
+ * error code
+ */
+ case REWRITE_ACTION_USER:
+ return_code = ((int *)action->la_args)[ 0 ];
+ goto rc_end_of_context;
+
+ 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;
+
+ /*
+ * A user-defined error code has propagated ...
+ */
+ default:
+ assert( rc >= REWRITE_REGEXEC_USER );
+ 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;
+}
+
+void
+rewrite_context_free(
+ void *tmp
+)
+{
+ struct rewrite_context *context = (struct rewrite_context *)tmp;
+
+ assert( tmp != NULL );
+
+ rewrite_context_destroy( &context );
+}
+
+int
+rewrite_context_destroy(
+ struct rewrite_context **pcontext
+)
+{
+ struct rewrite_context *context;
+ struct rewrite_rule *r;
+
+ assert( pcontext != NULL );
+ assert( *pcontext != NULL );
+
+ context = *pcontext;
+
+ assert( context->lc_rule != NULL );
+
+ for ( r = context->lc_rule->lr_next; r; ) {
+ struct rewrite_rule *cr = r;
+
+ r = r->lr_next;
+ rewrite_rule_destroy( &cr );
+ }
+
+ free( context->lc_rule );
+ context->lc_rule = NULL;
+
+ assert( context->lc_name != NULL );
+ free( context->lc_name );
+ context->lc_name = NULL;
+
+ free( context );
+ *pcontext = NULL;
+
+ return 0;
+}
diff --git a/libraries/librewrite/info.c b/libraries/librewrite/info.c
new file mode 100644
index 0000000..9db006d
--- /dev/null
+++ b/libraries/librewrite/info.c
@@ -0,0 +1,284 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#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 *rewrite_int_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 ...
+ */
+ rewrite_int_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_max_passes_per_rule = 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 ) ) {
+ ldap_avl_free( info->li_context, rewrite_context_free );
+ free( info );
+ return NULL;
+ }
+ if ( ldap_pvt_thread_rdwr_init( &info->li_params_mutex ) ) {
+ ldap_pvt_thread_rdwr_destroy( &info->li_cookies_mutex );
+ ldap_avl_free( info->li_context, rewrite_context_free );
+ free( info );
+ return NULL;
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return info;
+}
+
+/*
+ * Cleans up the info structure
+ */
+int
+rewrite_info_delete(
+ struct rewrite_info **pinfo
+)
+{
+ struct rewrite_info *info;
+
+ assert( pinfo != NULL );
+ assert( *pinfo != NULL );
+
+ info = *pinfo;
+
+ if ( info->li_context ) {
+ ldap_avl_free( info->li_context, rewrite_context_free );
+ }
+ info->li_context = NULL;
+
+ if ( info->li_maps ) {
+ ldap_avl_free( info->li_maps, rewrite_builtin_map_free );
+ }
+ info->li_maps = NULL;
+
+ rewrite_session_destroy( info );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_destroy( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rewrite_param_destroy( info );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_destroy( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ free( info );
+ *pinfo = NULL;
+
+ 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 };
+ 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 = ( *result != NULL ) ? REWRITE_REGEXEC_OK : REWRITE_REGEXEC_ERR;
+ goto rc_return;
+
+ case REWRITE_MODE_USE_DEFAULT:
+ context = rewrite_context_find( info,
+ REWRITE_DEFAULT_CONTEXT );
+ break;
+ }
+ }
+
+#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */
+ op.lo_string = strdup( string );
+ if ( op.lo_string == NULL ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto rc_return;
+ }
+#endif
+
+ /*
+ * Applies rewrite context
+ */
+ rc = rewrite_context_apply( info, &op, context, string, result );
+ assert( op.lo_depth == 0 );
+
+#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */
+ free( op.lo_string );
+#endif
+
+ 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:
+ if ( *result != NULL ) {
+ if ( *result != string ) {
+ free( *result );
+ }
+ *result = NULL;
+ }
+
+ default:
+ break;
+ }
+
+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 0000000..7d0252b
--- /dev/null
+++ b/libraries/librewrite/ldapmap.c
@@ -0,0 +1,454 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#define LDAP_DEPRECATED 1
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+typedef enum {
+ MAP_LDAP_UNKNOWN,
+ MAP_LDAP_EVERYTIME,
+ MAP_LDAP_NOW,
+ MAP_LDAP_LATER
+} bindwhen_t;
+
+/*
+ * LDAP map data structure
+ */
+struct ldap_map_data {
+ char *lm_url;
+ LDAPURLDesc *lm_lud;
+ int lm_version;
+ char *lm_binddn;
+ struct berval lm_cred;
+
+ bindwhen_t lm_when;
+
+ LDAP *lm_ld;
+
+ int lm_wantdn;
+ char *lm_attrs[ 2 ];
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_t lm_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+};
+
+static void
+map_ldap_free(
+ struct ldap_map_data *data
+)
+{
+ assert( data != NULL );
+
+ if ( data->lm_url != NULL ) {
+ free( data->lm_url );
+ }
+
+ if ( data->lm_lud != NULL ) {
+ ldap_free_urldesc( data->lm_lud );
+ }
+
+ if ( data->lm_binddn != NULL ) {
+ free( data->lm_binddn );
+ }
+
+ if ( data->lm_cred.bv_val != NULL ) {
+ memset( data->lm_cred.bv_val, 0, data->lm_cred.bv_len );
+ free( data->lm_cred.bv_val );
+ data->lm_cred.bv_val = NULL;
+ data->lm_cred.bv_len = 0;
+ }
+
+ if ( data->lm_when != MAP_LDAP_EVERYTIME && data->lm_ld != NULL ) {
+ ldap_unbind_ext( data->lm_ld, NULL, NULL );
+ }
+
+ free( data );
+}
+
+static void *
+map_ldap_parse(
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ struct ldap_map_data *data;
+ char *p, *uri;
+
+ 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",
+ fname, lineno );
+ free( data );
+ return NULL;
+ }
+
+ uri = argv[ 0 ];
+ if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) {
+ uri += STRLENOF( "uri=" );
+ }
+
+ data->lm_url = strdup( uri );
+ if ( data->lm_url == NULL ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ if ( ldap_url_parse( uri, &data->lm_lud ) != REWRITE_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] illegal URI '%s'\n",
+ fname, lineno, argv[ 0 ] );
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ /* trim everything after [host][:port] */
+ p = strchr( data->lm_url, '/' );
+ assert( p[ 1 ] == '/' );
+ if ( ( p = strchr( p + 2, '/' ) ) != NULL ) {
+ p[ 0 ] = '\0';
+ }
+
+ if ( data->lm_lud->lud_attrs == NULL ) {
+ data->lm_attrs[ 0 ] = LDAP_NO_ATTRS;
+ data->lm_wantdn = 1;
+
+ } else {
+ if ( data->lm_lud->lud_attrs[ 1 ] != NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] only one attribute allowed in URI\n",
+ fname, lineno );
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ if ( strcasecmp( data->lm_lud->lud_attrs[ 0 ], "dn" ) == 0
+ || strcasecmp( data->lm_lud->lud_attrs[ 0 ], "entryDN" ) == 0 )
+ {
+ ldap_memfree( data->lm_lud->lud_attrs[ 0 ] );
+ ldap_memfree( data->lm_lud->lud_attrs );
+ data->lm_lud->lud_attrs = NULL;
+ data->lm_attrs[ 0 ] = LDAP_NO_ATTRS;
+ data->lm_wantdn = 1;
+
+ } else {
+ data->lm_attrs[ 0 ] = data->lm_lud->lud_attrs[ 0 ];
+ }
+ }
+
+ data->lm_attrs[ 1 ] = NULL;
+
+ /* safe defaults */
+ data->lm_version = LDAP_VERSION3;
+
+ for ( argc--, argv++; argc > 0; argc--, argv++ ) {
+ if ( strncasecmp( argv[ 0 ], "binddn=", STRLENOF( "binddn=" ) ) == 0 ) {
+ char *p = argv[ 0 ] + STRLENOF( "binddn=" );
+ 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->lm_binddn = strdup( p );
+ if ( data->lm_binddn == NULL ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ if ( data->lm_binddn[ l ] == '\"'
+ || data->lm_binddn[ l ] == '\'' ) {
+ data->lm_binddn[ l ] = '\0';
+ }
+
+ /* deprecated */
+ } else if ( strncasecmp( argv[ 0 ], "bindpw=", STRLENOF( "bindpw=" ) ) == 0 ) {
+ ber_str2bv( argv[ 0 ] + STRLENOF( "bindpw=" ), 0, 1, &data->lm_cred );
+ if ( data->lm_cred.bv_val == NULL ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ } else if ( strncasecmp( argv[ 0 ], "credentials=", STRLENOF( "credentials=" ) ) == 0 ) {
+ ber_str2bv( argv[ 0 ] + STRLENOF( "credentials=" ), 0, 1, &data->lm_cred );
+ if ( data->lm_cred.bv_val == NULL ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ } else if ( strncasecmp( argv[ 0 ], "bindwhen=", STRLENOF( "bindwhen=" ) ) == 0 ) {
+ char *p = argv[ 0 ] + STRLENOF( "bindwhen=" );
+
+ if ( strcasecmp( p, "now" ) == 0 ) {
+ int rc;
+
+ data->lm_when = MAP_LDAP_NOW;
+
+ /*
+ * Init LDAP handler ...
+ */
+ rc = ldap_initialize( &data->lm_ld, data->lm_url );
+ if ( rc != LDAP_SUCCESS ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ ldap_set_option( data->lm_ld,
+ LDAP_OPT_PROTOCOL_VERSION,
+ (void *)&data->lm_version );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_init( &data->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ } else if ( strcasecmp( p, "later" ) == 0 ) {
+ data->lm_when = MAP_LDAP_LATER;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_init( &data->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ } else if ( strcasecmp( p, "everytime" ) == 0 ) {
+ data->lm_when = MAP_LDAP_EVERYTIME;
+ } else {
+ /* ignore ... */
+ }
+
+ } else if ( strncasecmp( argv[ 0 ], "version=", STRLENOF( "version=" ) ) == 0 ) {
+ if ( lutil_atoi( &data->lm_version, argv[ 0 ] + STRLENOF( "version=" ) ) ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ switch ( data->lm_version ) {
+ case LDAP_VERSION2:
+ case LDAP_VERSION3:
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unknown version %s\n",
+ fname, lineno, p );
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unknown option %s (ignored)\n",
+ fname, lineno, argv[0] );
+ }
+ }
+
+ if ( data->lm_when == MAP_LDAP_UNKNOWN ) {
+ data->lm_when = MAP_LDAP_EVERYTIME;
+ }
+
+ return ( void * )data;
+}
+
+static int
+map_ldap_apply(
+ void *private,
+ const char *filter,
+ struct berval *val
+
+)
+{
+ LDAP *ld;
+ LDAPMessage *res = NULL, *entry;
+ int rc;
+ struct ldap_map_data *data = private;
+ LDAPURLDesc *lud = data->lm_lud;
+
+ int first_try = 1, set_version = 0;
+
+ assert( private != NULL );
+ assert( filter != NULL );
+ assert( val != NULL );
+
+ val->bv_val = NULL;
+ val->bv_len = 0;
+
+ if ( data->lm_when == MAP_LDAP_EVERYTIME ) {
+ rc = ldap_initialize( &ld, data->lm_url );
+ set_version = 1;
+
+ } else {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &data->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rc = LDAP_SUCCESS;
+
+ if ( data->lm_when == MAP_LDAP_LATER && data->lm_ld == NULL ) {
+ rc = ldap_initialize( &data->lm_ld, data->lm_url );
+ set_version = 1;
+ }
+
+ ld = data->lm_ld;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+do_bind:;
+ if ( set_version ) {
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ (void *)&data->lm_version );
+ set_version = 0;
+ }
+
+ if ( data->lm_binddn != NULL ) {
+ rc = ldap_sasl_bind_s( ld, data->lm_binddn,
+ LDAP_SASL_SIMPLE, &data->lm_cred,
+ NULL, NULL, NULL );
+ if ( rc == LDAP_SERVER_DOWN && first_try ) {
+ first_try = 0;
+ if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ set_version = 1;
+ goto do_bind;
+
+ } else if ( rc != REWRITE_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ }
+
+ rc = ldap_search_ext_s( ld, lud->lud_dn, lud->lud_scope, ( char * )filter,
+ data->lm_attrs, 0, NULL, NULL, NULL, 1, &res );
+ if ( rc == LDAP_SERVER_DOWN && first_try ) {
+ first_try = 0;
+ if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ set_version = 1;
+ goto do_bind;
+
+ } else if ( rc != LDAP_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->lm_wantdn == 1 ) {
+ /*
+ * dn is newly allocated, so there's no need to strdup it
+ */
+ val->bv_val = ldap_get_dn( ld, entry );
+ val->bv_len = strlen( val->bv_val );
+
+ } else {
+ struct berval **values;
+
+ values = ldap_get_values_len( ld, entry, data->lm_attrs[ 0 ] );
+ if ( values != NULL ) {
+ if ( values[ 0 ] != NULL && values[ 0 ]->bv_val != NULL ) {
+#if 0
+ /* NOTE: in principle, multiple values
+ * should not be acceptable according
+ * to the current API; ignore by now */
+ if ( values[ 1 ] != NULL ) {
+ /* error */
+ }
+#endif
+ ber_dupbv( val, values[ 0 ] );
+ }
+ ldap_value_free_len( values );
+ }
+ }
+
+ ldap_msgfree( res );
+
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+rc_return:;
+ if ( data->lm_when == MAP_LDAP_EVERYTIME ) {
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+
+ } else {
+ data->lm_ld = ld;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &data->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ }
+
+ return rc;
+}
+
+static int
+map_ldap_destroy(
+ void *private
+)
+{
+ struct ldap_map_data *data = private;
+
+ assert( private != NULL );
+
+ map_ldap_free( data );
+
+ return 0;
+}
+
+const rewrite_mapper rewrite_ldap_mapper = {
+ "ldap",
+ map_ldap_parse,
+ map_ldap_apply,
+ map_ldap_destroy
+};
+
diff --git a/libraries/librewrite/map.c b/libraries/librewrite/map.c
new file mode 100644
index 0000000..3fa5863
--- /dev/null
+++ b/libraries/librewrite/map.c
@@ -0,0 +1,582 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include <stdio.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+static int num_mappers;
+static const rewrite_mapper **mappers;
+#define MAPPER_ALLOC 8
+
+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, mtx = 0, rc = 0;
+
+ 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 ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) {
+ /*
+ * '%' 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( (unsigned char) p[ 1 ] ) && p[ 2 ] == '{' ) {
+ cnt++;
+ p++;
+ }
+
+ if ( p[ 1 ] != '\0' ) {
+ 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 );
+ if ( s == NULL ) {
+ return NULL;
+ }
+ AC_MEMCPY( 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 ) {
+ rc = -1;
+ goto cleanup;
+ }
+ 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( (unsigned char) p[ 0 ] ) ) {
+ rc = -1;
+ goto cleanup;
+ }
+ for ( p++; p[ 0 ] != '\0'; p++ ) {
+ if ( !isalnum( (unsigned char) p[ 0 ] ) ) {
+ rc = -1;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * 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 ) {
+ rc = -1;
+ goto cleanup;
+ }
+ end[ 0 ] = '\0';
+
+ /*
+ * Compile the substitution pattern of the map argument
+ */
+ subst = rewrite_subst_compile( info, begin );
+ if ( subst == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+ }
+
+ /*
+ * Create the map
+ */
+ map = calloc( sizeof( struct rewrite_map ), 1 );
+ if ( map == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ memset( map, 0, sizeof( struct rewrite_map ) );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) {
+ rc = -1;
+ goto cleanup;
+ }
+ ++mtx;
+#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 );
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ map->lm_data = rewrite_context_find( info, s + 1 );
+ if ( map->lm_data == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ /*
+ * External command (not implemented yet)
+ */
+ case REWRITE_OPERATOR_COMMAND: /* '|' */
+ rc = -1;
+ goto cleanup;
+
+ /*
+ * 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 );
+ }
+ }
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ 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 );
+ }
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ /*
+ * Parameter
+ */
+ case REWRITE_OPERATOR_PARAM_GET: /* '$' */
+ map->lm_type = REWRITE_MAP_GET_PARAM;
+ map->lm_name = strdup( s + 1 );
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ /*
+ * Built-in map
+ */
+ default:
+ map->lm_type = REWRITE_MAP_BUILTIN;
+ map->lm_name = strdup( s );
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ map->lm_data = rewrite_builtin_map_find( info, s );
+ if ( map->lm_data == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ }
+
+cleanup:
+ free( s );
+ if ( rc ) {
+ if ( subst != NULL ) {
+ free( subst );
+ }
+ if ( map ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( mtx ) {
+ ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( map->lm_name ) {
+ free( map->lm_name );
+ map->lm_name = NULL;
+ }
+ free( map );
+ map = NULL;
+ }
+ }
+
+ return map;
+}
+
+/*
+ * 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 ) {
+ if ( val->bv_val == key->bv_val ) {
+ val->bv_len = key->bv_len;
+ key->bv_val = NULL;
+ } else {
+ 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 ( rc == REWRITE_SUCCESS ) {
+ 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;
+ }
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+ }
+ 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;
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+ }
+ 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 ( rc == REWRITE_SUCCESS ) {
+ 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;
+ }
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+ }
+ 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;
+
+ if ( bmap->lb_mapper && bmap->lb_mapper->rm_apply )
+ rc = bmap->lb_mapper->rm_apply( bmap->lb_private, key->bv_val,
+ val );
+ else
+ rc = REWRITE_ERR;
+ break;
+ }
+
+ default:
+ rc = REWRITE_ERR;
+ break;
+ }
+
+ return rc;
+}
+
+void
+rewrite_builtin_map_free(
+ void *tmp
+)
+{
+ struct rewrite_builtin_map *map = ( struct rewrite_builtin_map * )tmp;
+
+ assert( map != NULL );
+
+ if ( map->lb_mapper && map->lb_mapper->rm_destroy )
+ map->lb_mapper->rm_destroy( map->lb_private );
+
+ free( map->lb_name );
+ free( map );
+}
+
+int
+rewrite_map_destroy(
+ struct rewrite_map **pmap
+)
+{
+ struct rewrite_map *map;
+
+ assert( pmap != NULL );
+ assert( *pmap != NULL );
+
+ map = *pmap;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( map->lm_name ) {
+ free( map->lm_name );
+ map->lm_name = NULL;
+ }
+
+ if ( map->lm_subst ) {
+ rewrite_subst_destroy( &map->lm_subst );
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+ ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ free( map );
+ *pmap = NULL;
+
+ return 0;
+}
+
+/* ldapmap.c */
+extern const rewrite_mapper rewrite_ldap_mapper;
+
+const rewrite_mapper *
+rewrite_mapper_find(
+ const char *name
+)
+{
+ int i;
+
+ if ( !strcasecmp( name, "ldap" ))
+ return &rewrite_ldap_mapper;
+
+ for (i=0; i<num_mappers; i++)
+ if ( !strcasecmp( name, mappers[i]->rm_name ))
+ return mappers[i];
+ return NULL;
+}
+
+int
+rewrite_mapper_register(
+ const rewrite_mapper *map
+)
+{
+ if ( num_mappers % MAPPER_ALLOC == 0 ) {
+ const rewrite_mapper **mnew;
+ mnew = realloc( mappers, (num_mappers + MAPPER_ALLOC) *
+ sizeof( rewrite_mapper * ));
+ if ( mnew )
+ mappers = mnew;
+ else
+ return -1;
+ }
+ mappers[num_mappers++] = map;
+ return 0;
+}
+
+int
+rewrite_mapper_unregister(
+ const rewrite_mapper *map
+)
+{
+ int i;
+
+ for (i = 0; i<num_mappers; i++) {
+ if ( mappers[i] == map ) {
+ num_mappers--;
+ mappers[i] = mappers[num_mappers];
+ mappers[num_mappers] = NULL;
+ return 0;
+ }
+ }
+ /* not found */
+ return -1;
+}
diff --git a/libraries/librewrite/params.c b/libraries/librewrite/params.c
new file mode 100644
index 0000000..5da6e04
--- /dev/null
+++ b/libraries/librewrite/params.c
@@ -0,0 +1,147 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#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;
+ int rc = REWRITE_SUCCESS;
+
+ 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 || var->lv_value.bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return rc;
+}
+
+/*
+ * Gets a var with global scope
+ */
+int
+rewrite_param_get(
+ struct rewrite_info *info,
+ const char *name,
+ struct berval *value
+)
+{
+ struct rewrite_var *var;
+ int rc = REWRITE_SUCCESS;
+
+ 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 ) {
+ value->bv_val = strdup( var->lv_value.bv_val );
+ value->bv_len = var->lv_value.bv_len;
+ }
+
+ if ( var == NULL || value->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_runlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return rc;
+}
+
+static void
+rewrite_param_free(
+ void *tmp
+)
+{
+ struct rewrite_var *var = ( struct rewrite_var * )tmp;
+ 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 );
+ free( var );
+}
+
+/*
+ * Destroys the parameter tree
+ */
+int
+rewrite_param_destroy(
+ struct rewrite_info *info
+)
+{
+ assert( info != NULL );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ ldap_avl_free( info->li_params, rewrite_param_free );
+ 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 0000000..0053a26
--- /dev/null
+++ b/libraries/librewrite/parse.c
@@ -0,0 +1,124 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include <stdio.h>
+
+#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( (unsigned char) p[ 0 ] ); p++ );
+
+ if ( p[ 0 ] == '#' ) {
+ return 0;
+ }
+
+ for ( begin = p; p[ 0 ] != '\0'; p++ ) {
+ if ( p[ 0 ] == '\\' && p[ 1 ] != '\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( (unsigned char) 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( (unsigned char) p[ 0 ] ) && !in_quoted_field ) {
+ p[ 0 ] = '\0';
+ argv[ cnt ] = begin;
+
+ if ( ++cnt == maxargs ) {
+ *argc = cnt;
+ return 1;
+ }
+
+ for ( p++; isspace( (unsigned char) p[ 0 ] ); p++ );
+ begin = p;
+ p--;
+ }
+ }
+
+ *argc = cnt;
+
+ return 1;
+}
+
+int
+rewrite_read(
+ 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 0000000..441db51
--- /dev/null
+++ b/libraries/librewrite/rewrite-int.h
@@ -0,0 +1,628 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#ifndef REWRITE_INT_H
+#define REWRITE_INT_H
+
+/*
+ * These are required by every file of the library, so they're included here
+ */
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/ctype.h>
+
+#include <lber.h>
+#include <ldap.h>
+#define LDAP_DEFINE_LDAP_DEBUG
+#include <ldap_log.h>
+#include <lutil.h>
+#include <ldap_avl.h>
+
+#include <rewrite.h>
+
+#ifndef NO_THREADS
+#define USE_REWRITE_LDAP_PVT_THREADS
+#include <ldap_pvt_thread.h>
+#endif
+
+#define malloc(x) ber_memalloc(x)
+#define calloc(x,y) ber_memcalloc(x,y)
+#define realloc(x,y) ber_memrealloc(x,y)
+#define free(x) ber_memfree(x)
+#undef strdup
+#define strdup(x) ber_strdup(x)
+
+/*
+ * For details, see RATIONALE.
+ */
+
+#define REWRITE_MAX_MATCH 11 /* 0: overall string; 1-9: submatches */
+#define REWRITE_MAX_PASSES 100
+
+/*
+ * Submatch escape char
+ */
+/* the '\' conflicts with slapd.conf parsing */
+/* #define REWRITE_SUBMATCH_ESCAPE '\\' */
+#define REWRITE_SUBMATCH_ESCAPE_ORIG '%'
+#define REWRITE_SUBMATCH_ESCAPE '$'
+#define IS_REWRITE_SUBMATCH_ESCAPE(c) \
+ ((c) == REWRITE_SUBMATCH_ESCAPE || (c) == REWRITE_SUBMATCH_ESCAPE_ORIG)
+
+/*
+ * 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_USER 'U' /* requires an arg */
+#define REWRITE_FLAG_MAX_PASSES 'M' /* 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
+#define REWRITE_ACTION_USER 0x0005
+ 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 0x0200
+ int lb_type;
+ char *lb_name;
+ void *lb_private;
+ const rewrite_mapper *lb_mapper;
+
+#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;
+ int lr_max_passes;
+
+ 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;
+ ldap_pvt_thread_mutex_t ls_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ int ls_count;
+};
+
+/*
+ * Variable
+ */
+struct rewrite_var {
+ char *lv_name;
+ int lv_flags;
+ struct berval lv_value;
+};
+
+/*
+ * Operation
+ */
+struct rewrite_op {
+ int lo_num_passes;
+ int lo_depth;
+#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */
+ char *lo_string;
+#endif
+ 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;
+ int li_max_passes_per_rule;
+
+ /*
+ * Behavior in case a NULL or non-existent context is required
+ */
+ int li_rewrite_mode;
+};
+
+/***********
+ * PRIVATE *
+ ***********/
+
+LDAP_REWRITE_V (struct rewrite_context*) rewrite_int_curr_context;
+
+/*
+ * Maps
+ */
+
+/*
+ * Parses a map (also in legacy 'x' version)
+ */
+LDAP_REWRITE_F (struct rewrite_map *)
+rewrite_map_parse(
+ struct rewrite_info *info,
+ const char *s,
+ const char **end
+);
+
+LDAP_REWRITE_F (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)
+ */
+LDAP_REWRITE_F (int)
+rewrite_map_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_map *map,
+ struct berval *key,
+ struct berval *val
+);
+
+LDAP_REWRITE_F (int)
+rewrite_xmap_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_map *map,
+ struct berval *key,
+ struct berval *val
+);
+
+LDAP_REWRITE_F (int)
+rewrite_map_destroy(
+ struct rewrite_map **map
+);
+
+LDAP_REWRITE_F (int)
+rewrite_xmap_destroy(
+ struct rewrite_map **map
+);
+
+LDAP_REWRITE_F (void)
+rewrite_builtin_map_free(
+ void *map
+);
+/*
+ * Submatch substitution
+ */
+
+/*
+ * Compiles a substitution pattern
+ */
+LDAP_REWRITE_F (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
+ */
+LDAP_REWRITE_F (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
+);
+
+LDAP_REWRITE_F (int)
+rewrite_subst_destroy(
+ struct rewrite_subst **subst
+);
+
+
+/*
+ * Rules
+ */
+
+/*
+ * Compiles the rule and appends it at the running context
+ */
+LDAP_REWRITE_F (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
+ */
+LDAP_REWRITE_F (int)
+rewrite_rule_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_rule *rule,
+ const char *string,
+ char **result
+);
+
+LDAP_REWRITE_F (int)
+rewrite_rule_destroy(
+ struct rewrite_rule **rule
+);
+
+/*
+ * Sessions
+ */
+
+/*
+ * Fetches a struct rewrite_session
+ */
+LDAP_REWRITE_F (struct rewrite_session *)
+rewrite_session_find(
+ struct rewrite_info *info,
+ const void *cookie
+);
+
+/*
+ * Defines and inits a variable with session scope
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_var_set_f(
+ struct rewrite_info *info,
+ const void *cookie,
+ const char *name,
+ const char *value,
+ int flags
+);
+
+/*
+ * Gets a var with session scope
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_var_get(
+ struct rewrite_info *info,
+ const void *cookie,
+ const char *name,
+ struct berval *val
+);
+
+/*
+ * Deletes a session
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_delete(
+ struct rewrite_info *info,
+ const void *cookie
+);
+
+/*
+ * Destroys the cookie tree
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_destroy(
+ struct rewrite_info *info
+);
+
+
+/*
+ * Vars
+ */
+
+/*
+ * Finds a var
+ */
+LDAP_REWRITE_F (struct rewrite_var *)
+rewrite_var_find(
+ Avlnode *tree,
+ const char *name
+);
+
+/*
+ * Replaces the value of a variable
+ */
+LDAP_REWRITE_F (int)
+rewrite_var_replace(
+ struct rewrite_var *var,
+ const char *value,
+ int flags
+);
+
+/*
+ * Inserts a newly created var
+ */
+LDAP_REWRITE_F (struct rewrite_var *)
+rewrite_var_insert_f(
+ Avlnode **tree,
+ const char *name,
+ const char *value,
+ int flags
+);
+
+#define rewrite_var_insert(tree, name, value) \
+ rewrite_var_insert_f((tree), (name), (value), \
+ REWRITE_VAR_UPDATE|REWRITE_VAR_COPY_NAME|REWRITE_VAR_COPY_VALUE)
+
+/*
+ * Sets/inserts a var
+ */
+LDAP_REWRITE_F (struct rewrite_var *)
+rewrite_var_set_f(
+ Avlnode **tree,
+ const char *name,
+ const char *value,
+ int flags
+);
+
+#define rewrite_var_set(tree, name, value, insert) \
+ rewrite_var_set_f((tree), (name), (value), \
+ REWRITE_VAR_UPDATE|REWRITE_VAR_COPY_NAME|REWRITE_VAR_COPY_VALUE|((insert)? REWRITE_VAR_INSERT : 0))
+
+/*
+ * Deletes a var tree
+ */
+LDAP_REWRITE_F (int)
+rewrite_var_delete(
+ Avlnode *tree
+);
+
+
+/*
+ * Contexts
+ */
+
+/*
+ * Finds the context named rewriteContext in the context tree
+ */
+LDAP_REWRITE_F (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
+ */
+LDAP_REWRITE_F (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'
+ */
+LDAP_REWRITE_F (int)
+rewrite_context_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_context *context,
+ const char *string,
+ char **result
+);
+
+LDAP_REWRITE_F (int)
+rewrite_context_destroy(
+ struct rewrite_context **context
+);
+
+LDAP_REWRITE_F (void)
+rewrite_context_free(
+ void *tmp
+);
+
+#endif /* REWRITE_INT_H */
+
diff --git a/libraries/librewrite/rewrite-map.h b/libraries/librewrite/rewrite-map.h
new file mode 100644
index 0000000..1a19679
--- /dev/null
+++ b/libraries/librewrite/rewrite-map.h
@@ -0,0 +1,32 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#ifndef MAP_H
+#define MAP_H
+
+/*
+ * Retrieves a builtin map
+ */
+LDAP_REWRITE_F (struct rewrite_builtin_map *)
+rewrite_builtin_map_find(
+ struct rewrite_info *info,
+ const char *name
+);
+
+#endif /* MAP_H */
diff --git a/libraries/librewrite/rewrite.c b/libraries/librewrite/rewrite.c
new file mode 100644
index 0000000..9c35975
--- /dev/null
+++ b/libraries/librewrite/rewrite.c
@@ -0,0 +1,195 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <stdio.h>
+
+#include <rewrite.h>
+#include <lutil.h>
+#include <ldap.h>
+
+int ldap_debug;
+int ldap_syslog;
+int ldap_syslog_level;
+
+static void
+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 ( rewrite_read( fin, info ) != 0 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ rewrite_param_set( info, "prog", "rewrite" );
+
+ rewrite_session_init( info, cookie );
+
+ string = (char *)arg;
+ for ( sep = strchr( rewriteContext, ',' );
+ rewriteContext != NULL;
+ rewriteContext = sep,
+ sep ? sep = strchr( rewriteContext, ',' ) : NULL )
+ {
+ char *errmsg = "";
+
+ if ( sep != NULL ) {
+ sep[ 0 ] = '\0';
+ sep++;
+ }
+ /* rc = rewrite( info, rewriteContext, string, &result ); */
+ rc = rewrite_session( info, rewriteContext, string,
+ cookie, &result );
+
+ switch ( rc ) {
+ case REWRITE_REGEXEC_OK:
+ errmsg = "ok";
+ break;
+
+ case REWRITE_REGEXEC_ERR:
+ errmsg = "error";
+ break;
+
+ case REWRITE_REGEXEC_STOP:
+ errmsg = "stop";
+ break;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ errmsg = "unwilling to perform";
+ break;
+
+ default:
+ if (rc >= REWRITE_REGEXEC_USER) {
+ errmsg = "user-defined";
+ } else {
+ errmsg = "unknown";
+ }
+ break;
+ }
+
+ fprintf( stdout, "%s -> %s [%d:%s]\n", string,
+ ( result ? result : "(null)" ),
+ rc, errmsg );
+ if ( result == NULL ) {
+ break;
+ }
+ if ( string != arg && string != result ) {
+ free( string );
+ }
+ string = result;
+ }
+
+ if ( result && result != arg ) {
+ free( result );
+ }
+
+ rewrite_session_delete( info, cookie );
+
+ rewrite_info_delete( &info );
+}
+
+int
+main( int argc, char *argv[] )
+{
+ FILE *fin = NULL;
+ char *rewriteContext = REWRITE_DEFAULT_CONTEXT;
+ int debug = 0;
+
+ while ( 1 ) {
+ int opt = getopt( argc, argv, "d:f:hr:" );
+
+ if ( opt == EOF ) {
+ break;
+ }
+
+ switch ( opt ) {
+ case 'd':
+ if ( lutil_atoi( &debug, optarg ) != 0 ) {
+ fprintf( stderr, "illegal log level '%s'\n",
+ optarg );
+ exit( EXIT_FAILURE );
+ }
+ break;
+
+ 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 = optarg;
+ break;
+ }
+ }
+
+ if ( debug != 0 ) {
+ ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &debug);
+ ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
+ }
+
+ if ( optind >= argc ) {
+ return -1;
+ }
+
+ apply( ( fin ? fin : stdin ), rewriteContext, argv[ optind ] );
+
+ if ( fin ) {
+ fclose( fin );
+ }
+
+ return 0;
+}
+
diff --git a/libraries/librewrite/rule.c b/libraries/librewrite/rule.c
new file mode 100644
index 0000000..b4fe405
--- /dev/null
+++ b/libraries/librewrite/rule.c
@@ -0,0 +1,510 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#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 **pbase,
+ struct rewrite_action *action
+)
+{
+ struct rewrite_action **pa;
+
+ assert( pbase != NULL );
+ assert( action != NULL );
+
+ for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
+ *pa = action;
+
+ return REWRITE_SUCCESS;
+}
+
+static int
+destroy_action(
+ struct rewrite_action **paction
+)
+{
+ struct rewrite_action *action;
+
+ assert( paction != NULL );
+ assert( *paction != NULL );
+
+ action = *paction;
+
+ /* do something */
+ switch ( action->la_type ) {
+ case REWRITE_FLAG_GOTO:
+ case REWRITE_FLAG_USER: {
+ int *pi = (int *)action->la_args;
+
+ if ( pi ) {
+ free( pi );
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ free( action );
+ *paction = NULL;
+
+ return 0;
+}
+
+static void
+destroy_actions(
+ struct rewrite_action *paction
+)
+{
+ struct rewrite_action *next;
+
+ for (; paction; paction = next) {
+ next = paction->la_next;
+ destroy_action( &paction );
+ }
+}
+
+/*
+ */
+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;
+ int max_passes;
+
+ 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
+ */
+
+ max_passes = info->li_max_passes_per_rule;
+
+ /*
+ * 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 ) {
+ goto fail;
+ }
+
+ 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 ) {
+ goto fail;
+ }
+
+ 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
+ */
+
+ case REWRITE_FLAG_USER: { /* 'U' */
+ /*
+ * After applying rule, return user-defined
+ * error code
+ */
+ char *next = NULL;
+ int *d;
+
+ if ( p[ 1 ] != '{' ) {
+ goto fail;
+ }
+
+ d = malloc( sizeof( int ) );
+ if ( d == NULL ) {
+ goto fail;
+ }
+
+ d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
+ if ( next == &p[ 2 ] || next[0] != '}' ) {
+ free( d );
+ goto fail;
+ }
+
+ action = calloc( sizeof( struct rewrite_action ), 1 );
+ if ( action == NULL ) {
+ free( d );
+ goto fail;
+ }
+ switch ( p[ 0 ] ) {
+ case REWRITE_FLAG_GOTO:
+ action->la_type = REWRITE_ACTION_GOTO;
+ break;
+
+ case REWRITE_FLAG_USER:
+ action->la_type = REWRITE_ACTION_USER;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ action->la_args = (void *)d;
+
+ p = next; /* p is incremented by the for ... */
+
+ break;
+ }
+
+ case REWRITE_FLAG_MAX_PASSES: { /* 'U' */
+ /*
+ * Set the number of max passes per rule
+ */
+ char *next = NULL;
+
+ if ( p[ 1 ] != '{' ) {
+ goto fail;
+ }
+
+ max_passes = strtol( &p[ 2 ], &next, 0 );
+ if ( next == &p[ 2 ] || next[0] != '}' ) {
+ goto fail;
+ }
+
+ if ( max_passes < 1 ) {
+ /* FIXME: nonsense ... */
+ max_passes = 1;
+ }
+
+ p = next; /* 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 ) {
+ goto fail;
+ }
+
+ 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 ) {
+ append_action( &first_action, action );
+ action = NULL;
+ }
+ }
+
+ /*
+ * Finally, rule allocation
+ */
+ rule = calloc( sizeof( struct rewrite_rule ), 1 );
+ if ( rule == NULL ) {
+ goto fail;
+ }
+
+ /*
+ * REGEX compilation (luckily I don't need to take care of this ...)
+ */
+ if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
+ goto fail;
+ }
+
+ /*
+ * Just to remember them ...
+ */
+ rule->lr_pattern = strdup( pattern );
+ rule->lr_subststring = strdup( result );
+ rule->lr_flagstring = strdup( flagstring );
+ if ( rule->lr_pattern == NULL
+ || rule->lr_subststring == NULL
+ || rule->lr_flagstring == NULL )
+ {
+ goto fail;
+ }
+
+ /*
+ * 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_max_passes = max_passes;
+ rule->lr_action = first_action;
+
+ /*
+ * Append rule at the end of the rewrite context
+ */
+ append_rule( context, rule );
+
+ return REWRITE_SUCCESS;
+
+fail:
+ if ( rule ) {
+ if ( rule->lr_pattern ) free( rule->lr_pattern );
+ if ( rule->lr_subststring ) free( rule->lr_subststring );
+ if ( rule->lr_flagstring ) free( rule->lr_flagstring );
+ free( rule );
+ }
+ destroy_actions( first_action );
+ free( subst );
+ return REWRITE_ERR;
+}
+
+/*
+ * 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;
+ int strcnt = 0;
+ struct berval val = { 0, NULL };
+
+ assert( info != NULL );
+ assert( op != NULL );
+ assert( rule != NULL );
+ assert( arg != NULL );
+ assert( result != NULL );
+
+ *result = NULL;
+
+ string = (char *)arg;
+
+ /*
+ * In case recursive match is required (default)
+ */
+recurse:;
+
+ Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
+ " rule='%s' string='%s' [%d pass(es)]\n",
+ rule->lr_pattern, string, strcnt + 1 );
+
+ op->lo_num_passes++;
+
+ rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
+ if ( rc != 0 ) {
+ if ( *result == NULL && string != arg ) {
+ 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;
+ val.bv_val = NULL;
+ if ( string != arg ) {
+ free( string );
+ string = NULL;
+ }
+
+ if ( rc != REWRITE_REGEXEC_OK ) {
+ return rc;
+ }
+
+ if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE
+ && op->lo_num_passes < info->li_max_passes
+ && ++strcnt < rule->lr_max_passes ) {
+ string = *result;
+
+ goto recurse;
+ }
+
+ return REWRITE_REGEXEC_OK;
+}
+
+int
+rewrite_rule_destroy(
+ struct rewrite_rule **prule
+ )
+{
+ struct rewrite_rule *rule;
+
+ assert( prule != NULL );
+ assert( *prule != NULL );
+
+ rule = *prule;
+
+ if ( rule->lr_pattern ) {
+ free( rule->lr_pattern );
+ rule->lr_pattern = NULL;
+ }
+
+ if ( rule->lr_subststring ) {
+ free( rule->lr_subststring );
+ rule->lr_subststring = NULL;
+ }
+
+ if ( rule->lr_flagstring ) {
+ free( rule->lr_flagstring );
+ rule->lr_flagstring = NULL;
+ }
+
+ if ( rule->lr_subst ) {
+ rewrite_subst_destroy( &rule->lr_subst );
+ }
+
+ regfree( &rule->lr_regex );
+
+ destroy_actions( rule->lr_action );
+
+ free( rule );
+ *prule = NULL;
+
+ return 0;
+}
+
diff --git a/libraries/librewrite/session.c b/libraries/librewrite/session.c
new file mode 100644
index 0000000..f766159
--- /dev/null
+++ b/libraries/librewrite/session.c
@@ -0,0 +1,427 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compares two cookies
+ */
+static int
+rewrite_cookie_cmp(
+ const void *c1,
+ const void *c2
+)
+{
+ const struct rewrite_session *s1, *s2;
+
+ s1 = ( const struct rewrite_session * )c1;
+ s2 = ( const 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 );
+
+ assert( s1->ls_cookie != s2->ls_cookie );
+
+ 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, tmp;
+ int rc;
+
+ assert( info != NULL );
+ assert( cookie != NULL );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ tmp.ls_cookie = ( void * )cookie;
+ session = ( struct rewrite_session * )ldap_avl_find( info->li_cookies,
+ ( caddr_t )&tmp, rewrite_cookie_cmp );
+ if ( session ) {
+ session->ls_count++;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ return session;
+ }
+
+ session = calloc( sizeof( struct rewrite_session ), 1 );
+ if ( session == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ return NULL;
+ }
+ session->ls_cookie = ( void * )cookie;
+ session->ls_count = 1;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( ldap_pvt_thread_mutex_init( &session->ls_mutex ) ) {
+ free( session );
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+ return NULL;
+ }
+ if ( ldap_pvt_thread_rdwr_init( &session->ls_vars_mutex ) ) {
+ ldap_pvt_thread_mutex_destroy( &session->ls_mutex );
+ free( session );
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+ return NULL;
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rc = ldap_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 ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_destroy( &session->ls_vars_mutex );
+ ldap_pvt_thread_mutex_destroy( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ 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 * )ldap_avl_find( info->li_cookies,
+ ( caddr_t )&tmp, rewrite_cookie_cmp );
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( session ) {
+ ldap_pvt_thread_mutex_lock( &session->ls_mutex );
+ session->ls_count++;
+ }
+ ldap_pvt_thread_rdwr_runlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return session;
+}
+
+/*
+ * Returns a session
+ */
+void
+rewrite_session_return(
+ struct rewrite_info *info,
+ struct rewrite_session *session
+)
+{
+ assert( session != NULL );
+ session->ls_count--;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+}
+
+/*
+ * Defines and inits a var with session scope
+ */
+int
+rewrite_session_var_set_f(
+ struct rewrite_info *info,
+ const void *cookie,
+ const char *name,
+ const char *value,
+ int flags
+)
+{
+ 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 );
+ if ( session == NULL ) {
+ return REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ }
+
+#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 );
+
+ (void)rewrite_var_replace( var, value, flags );
+
+ } else {
+ var = rewrite_var_insert_f( &session->ls_vars, name, value, flags );
+ if ( var == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ rewrite_session_return( info, session );
+ return REWRITE_ERR;
+ }
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rewrite_session_return( info, session );
+
+ 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;
+ int rc = REWRITE_SUCCESS;
+
+ 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 ) {
+ value->bv_val = strdup( var->lv_value.bv_val );
+ value->bv_len = var->lv_value.bv_len;
+ }
+
+ if ( var == NULL || value->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_runlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rewrite_session_return( info, session );
+
+ return rc;
+}
+
+static void
+rewrite_session_clean( void *v_session )
+{
+ struct rewrite_session *session = (struct rewrite_session *)v_session;
+
+#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 );
+ ldap_pvt_thread_rdwr_destroy( &session->ls_vars_mutex );
+ ldap_pvt_thread_mutex_unlock( &session->ls_mutex );
+ ldap_pvt_thread_mutex_destroy( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+}
+
+static void
+rewrite_session_free( void *v_session )
+{
+ struct rewrite_session *session = (struct rewrite_session *)v_session;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ rewrite_session_clean( v_session );
+ free( v_session );
+}
+
+/*
+ * Deletes a session
+ */
+int
+rewrite_session_delete(
+ struct rewrite_info *info,
+ const void *cookie
+)
+{
+ struct rewrite_session *session, tmp = { 0 };
+
+ assert( info != NULL );
+ assert( cookie != NULL );
+
+ session = rewrite_session_find( info, cookie );
+
+ if ( session == NULL ) {
+ return REWRITE_SUCCESS;
+ }
+
+ if ( --session->ls_count > 0 ) {
+ rewrite_session_return( info, session );
+ return REWRITE_SUCCESS;
+ }
+
+ rewrite_session_clean( session );
+
+#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
+ */
+ tmp.ls_cookie = ( void * )cookie;
+ ldap_avl_delete( &info->li_cookies, ( caddr_t )&tmp, rewrite_cookie_cmp );
+
+ free( session );
+
+#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 = ldap_avl_free( info->li_cookies, rewrite_session_free );
+ info->li_cookies = NULL;
+
+#if 0
+ fprintf( stderr, "count = %d; num_cookies = %d\n",
+ count, info->li_num_cookies );
+#endif
+
+ 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 0000000..16c13ec
--- /dev/null
+++ b/libraries/librewrite/subst.c
@@ -0,0 +1,513 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compiles a substitution pattern
+ */
+struct rewrite_subst *
+rewrite_subst_compile(
+ struct rewrite_info *info,
+ const char *str
+)
+{
+ size_t subs_len;
+ struct berval *subs = NULL, *tmps;
+ struct rewrite_submatch *submatch = NULL, *tmpsm;
+
+ struct rewrite_subst *s = NULL;
+
+ char *result, *begin, *p;
+ int nsub = 0, l;
+
+ assert( info != NULL );
+ assert( str != NULL );
+
+ result = strdup( str );
+ if ( result == NULL ) {
+ return NULL;
+ }
+
+ /*
+ * Take care of substitution string
+ */
+ for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) {
+
+ /*
+ * Keep only single escapes '%'
+ */
+ if ( !IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) {
+ continue;
+ }
+
+ if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 1 ] ) ) {
+ /* Pull &p[1] over p, including the trailing '\0' */
+ AC_MEMCPY((char *)p, &p[ 1 ], strlen( p ) );
+ continue;
+ }
+
+ tmps = ( struct berval * )realloc( subs,
+ sizeof( struct berval )*( nsub + 1 ) );
+ if ( tmps == NULL ) {
+ goto cleanup;
+ }
+ subs = tmps;
+ subs[ nsub ].bv_val = NULL;
+
+ tmpsm = ( struct rewrite_submatch * )realloc( submatch,
+ sizeof( struct rewrite_submatch )*( nsub + 1 ) );
+ if ( tmpsm == NULL ) {
+ goto cleanup;
+ }
+ submatch = tmpsm;
+ submatch[ nsub ].ls_map = 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 ].bv_len = l;
+ subs[ nsub ].bv_val = malloc( l + 1 );
+ if ( subs[ nsub ].bv_val == NULL ) {
+ goto cleanup;
+ }
+ AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
+ subs[ nsub ].bv_val[ l ] = '\0';
+ } else {
+ subs[ nsub ].bv_val = NULL;
+ subs[ nsub ].bv_len = 0;
+ }
+
+ /*
+ * Substitution pattern
+ */
+ if ( isdigit( (unsigned char) p[ 1 ] ) ) {
+ int d = p[ 1 ] - '0';
+
+ /*
+ * Add a new value substitution scheme
+ */
+
+ submatch[ nsub ].ls_submatch = d;
+
+ /*
+ * If there is no argument, use default
+ * (substitute substring as is)
+ */
+ if ( p[ 2 ] != '{' ) {
+ submatch[ nsub ].ls_type =
+ REWRITE_SUBMATCH_ASIS;
+ submatch[ nsub ].ls_map = NULL;
+ begin = ++p + 1;
+
+ } else {
+ struct rewrite_map *map;
+
+ submatch[ nsub ].ls_type =
+ REWRITE_SUBMATCH_XMAP;
+
+ map = rewrite_xmap_parse( info,
+ p + 3, (const char **)&begin );
+ if ( map == NULL ) {
+ goto cleanup;
+ }
+ submatch[ nsub ].ls_map = map;
+ p = begin - 1;
+ }
+
+ /*
+ * Map with args ...
+ */
+ } else if ( p[ 1 ] == '{' ) {
+ struct rewrite_map *map;
+
+ map = rewrite_map_parse( info, p + 2,
+ (const char **)&begin );
+ if ( map == NULL ) {
+ goto cleanup;
+ }
+ p = begin - 1;
+
+ /*
+ * Add a new value substitution scheme
+ */
+ submatch[ nsub ].ls_type =
+ REWRITE_SUBMATCH_MAP_W_ARG;
+ submatch[ nsub ].ls_map = map;
+
+ /*
+ * Escape '%' ...
+ */
+ } else if ( p[ 1 ] == '%' ) {
+ AC_MEMCPY( &p[ 1 ], &p[ 2 ], strlen( &p[ 1 ] ) );
+ continue;
+
+ } else {
+ goto cleanup;
+ }
+
+ nsub++;
+ }
+
+ /*
+ * Last part of string
+ */
+ tmps = (struct berval * )realloc( subs, sizeof( struct berval )*( nsub + 1 ) );
+ if ( tmps == NULL ) {
+ /*
+ * XXX need to free the value subst stuff!
+ */
+ free( subs );
+ goto cleanup;
+ }
+ subs = tmps;
+ l = p - begin;
+ if ( l > 0 ) {
+ subs_len += l;
+ subs[ nsub ].bv_len = l;
+ subs[ nsub ].bv_val = malloc( l + 1 );
+ if ( subs[ nsub ].bv_val == NULL ) {
+ goto cleanup;
+ }
+ AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
+ subs[ nsub ].bv_val[ l ] = '\0';
+ } else {
+ subs[ nsub ].bv_val = NULL;
+ subs[ nsub ].bv_len = 0;
+ }
+
+ s = calloc( sizeof( struct rewrite_subst ), 1 );
+ if ( s == NULL ) {
+ goto cleanup;
+ }
+
+ s->lt_subs_len = subs_len;
+ s->lt_subs = subs;
+ s->lt_num_submatch = nsub;
+ s->lt_submatch = submatch;
+ subs = NULL;
+ submatch = NULL;
+
+cleanup:;
+ if ( subs ) {
+ for ( l=0; l<nsub; l++ ) {
+ free( subs[nsub].bv_val );
+ }
+ free( subs );
+ }
+ if ( submatch ) {
+ for ( l=0; l<nsub; l++ ) {
+ free( submatch[nsub].ls_map );
+ }
+ free( submatch );
+ }
+ free( result );
+
+ 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 );
+ assert( val->bv_val == NULL );
+
+ c = submatch->ls_submatch;
+ s = string + match[ c ].rm_so;
+ l = match[ c ].rm_eo - match[ c ].rm_so;
+
+ val->bv_len = l;
+ val->bv_val = malloc( l + 1 );
+ if ( val->bv_val == NULL ) {
+ return REWRITE_ERR;
+ }
+
+ AC_MEMCPY( 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 = 0, 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 );
+
+ assert( val->bv_val == NULL );
+
+ val->bv_val = NULL;
+ val->bv_len = 0;
+
+ /*
+ * Prepare room for submatch expansion
+ */
+ if ( subst->lt_num_submatch > 0 ) {
+ 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 = { 0, NULL };
+
+ submatch[ n ].bv_val = NULL;
+
+ /*
+ * 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 ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto cleanup;
+ }
+ 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 ) {
+ goto cleanup;
+ }
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "Not Implemented\n" );
+ rc = REWRITE_ERR;
+ break;
+ }
+
+ if ( rc != REWRITE_SUCCESS ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto cleanup;
+ }
+
+ /*
+ * 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 ] );
+ free( key.bv_val );
+ key.bv_val = NULL;
+ break;
+
+ case REWRITE_SUBMATCH_MAP_W_ARG:
+ rc = rewrite_map_apply( info, op,
+ subst->lt_submatch[ n ].ls_map,
+ &key, &submatch[ n ] );
+ free( key.bv_val );
+ key.bv_val = NULL;
+ 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 ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto cleanup;
+ }
+
+ /*
+ * Increment the length of the resulting string
+ */
+ l += submatch[ n ].bv_len;
+ }
+
+ /*
+ * Alloc result buffer
+ */
+ l += subst->lt_subs_len;
+ res = malloc( l + 1 );
+ if ( res == NULL ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto cleanup;
+ }
+
+ /*
+ * Apply submatches (possibly resolved thru maps)
+ */
+ for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) {
+ if ( subst->lt_subs[ n ].bv_val != NULL ) {
+ AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
+ subst->lt_subs[ n ].bv_len );
+ cl += subst->lt_subs[ n ].bv_len;
+ }
+ AC_MEMCPY( res + cl, submatch[ n ].bv_val,
+ submatch[ n ].bv_len );
+ cl += submatch[ n ].bv_len;
+ }
+ if ( subst->lt_subs[ n ].bv_val != NULL ) {
+ AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
+ subst->lt_subs[ n ].bv_len );
+ cl += subst->lt_subs[ n ].bv_len;
+ }
+ res[ cl ] = '\0';
+
+ val->bv_val = res;
+ val->bv_len = l;
+
+cleanup:;
+ if ( submatch ) {
+ for ( ; --n >= 0; ) {
+ if ( submatch[ n ].bv_val ) {
+ free( submatch[ n ].bv_val );
+ }
+ }
+ free( submatch );
+ }
+
+ return rc;
+}
+
+/*
+ * frees data
+ */
+int
+rewrite_subst_destroy(
+ struct rewrite_subst **psubst
+)
+{
+ int n;
+ struct rewrite_subst *subst;
+
+ assert( psubst != NULL );
+ assert( *psubst != NULL );
+
+ subst = *psubst;
+
+ for ( n = 0; n < subst->lt_num_submatch; n++ ) {
+ if ( subst->lt_subs[ n ].bv_val ) {
+ free( subst->lt_subs[ n ].bv_val );
+ subst->lt_subs[ n ].bv_val = NULL;
+ }
+
+ switch ( subst->lt_submatch[ n ].ls_type ) {
+ case REWRITE_SUBMATCH_ASIS:
+ break;
+
+ case REWRITE_SUBMATCH_XMAP:
+ rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map );
+ break;
+
+ case REWRITE_SUBMATCH_MAP_W_ARG:
+ rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ free( subst->lt_submatch );
+ subst->lt_submatch = NULL;
+
+ /* last one */
+ if ( subst->lt_subs[ n ].bv_val ) {
+ free( subst->lt_subs[ n ].bv_val );
+ subst->lt_subs[ n ].bv_val = NULL;
+ }
+
+ free( subst->lt_subs );
+ subst->lt_subs = NULL;
+
+ free( subst );
+ *psubst = NULL;
+
+ return 0;
+}
+
diff --git a/libraries/librewrite/var.c b/libraries/librewrite/var.c
new file mode 100644
index 0000000..89a4b01
--- /dev/null
+++ b/libraries/librewrite/var.c
@@ -0,0 +1,273 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#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 = ( const struct rewrite_var * )c1;
+ v2 = ( const 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 );
+}
+
+/*
+ * Frees a var
+ */
+static void
+rewrite_var_free(
+ void *v_var
+)
+{
+ struct rewrite_var *var = v_var;
+ assert( var != NULL );
+
+ assert( var->lv_name != NULL );
+ assert( var->lv_value.bv_val != NULL );
+
+ if ( var->lv_flags & REWRITE_VAR_COPY_NAME )
+ free( var->lv_name );
+ if ( var->lv_flags & REWRITE_VAR_COPY_VALUE )
+ free( var->lv_value.bv_val );
+ free( var );
+}
+
+/*
+ * Deletes a var tree
+ */
+int
+rewrite_var_delete(
+ Avlnode *tree
+)
+{
+ ldap_avl_free( tree, rewrite_var_free );
+ return REWRITE_SUCCESS;
+}
+
+/*
+ * 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 * )ldap_avl_find( tree,
+ ( caddr_t )&var, rewrite_var_cmp );
+}
+
+int
+rewrite_var_replace(
+ struct rewrite_var *var,
+ const char *value,
+ int flags
+)
+{
+ ber_len_t len;
+
+ assert( value != NULL );
+
+ len = strlen( value );
+
+ if ( var->lv_flags & REWRITE_VAR_COPY_VALUE ) {
+ if ( flags & REWRITE_VAR_COPY_VALUE ) {
+ if ( len <= var->lv_value.bv_len ) {
+ AC_MEMCPY(var->lv_value.bv_val, value, len + 1);
+
+ } else {
+ free( var->lv_value.bv_val );
+ var->lv_value.bv_val = strdup( value );
+ }
+
+ } else {
+ free( var->lv_value.bv_val );
+ var->lv_value.bv_val = (char *)value;
+ var->lv_flags &= ~REWRITE_VAR_COPY_VALUE;
+ }
+
+ } else {
+ if ( flags & REWRITE_VAR_COPY_VALUE ) {
+ var->lv_value.bv_val = strdup( value );
+ var->lv_flags |= REWRITE_VAR_COPY_VALUE;
+
+ } else {
+ var->lv_value.bv_val = (char *)value;
+ }
+ }
+
+ if ( var->lv_value.bv_val == NULL ) {
+ return -1;
+ }
+
+ var->lv_value.bv_len = len;
+
+ return 0;
+}
+
+/*
+ * Inserts a newly created var
+ */
+struct rewrite_var *
+rewrite_var_insert_f(
+ Avlnode **tree,
+ const char *name,
+ const char *value,
+ int flags
+)
+{
+ struct rewrite_var *var;
+ int rc = 0;
+
+ assert( tree != NULL );
+ assert( name != NULL );
+ assert( value != NULL );
+
+ var = rewrite_var_find( *tree, name );
+ if ( var != NULL ) {
+ if ( flags & REWRITE_VAR_UPDATE ) {
+ (void)rewrite_var_replace( var, value, flags );
+ goto cleanup;
+ }
+ rc = -1;
+ goto cleanup;
+ }
+
+ var = calloc( sizeof( struct rewrite_var ), 1 );
+ if ( var == NULL ) {
+ return NULL;
+ }
+
+ memset( var, 0, sizeof( struct rewrite_var ) );
+
+ if ( flags & REWRITE_VAR_COPY_NAME ) {
+ var->lv_name = strdup( name );
+ if ( var->lv_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ var->lv_flags |= REWRITE_VAR_COPY_NAME;
+
+ } else {
+ var->lv_name = (char *)name;
+ }
+
+ if ( flags & REWRITE_VAR_COPY_VALUE ) {
+ var->lv_value.bv_val = strdup( value );
+ if ( var->lv_value.bv_val == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ var->lv_flags |= REWRITE_VAR_COPY_VALUE;
+
+ } else {
+ var->lv_value.bv_val = (char *)value;
+ }
+ var->lv_value.bv_len = strlen( value );
+ rc = ldap_avl_insert( tree, ( caddr_t )var,
+ rewrite_var_cmp, rewrite_var_dup );
+
+cleanup:;
+ if ( rc != 0 && var ) {
+ ldap_avl_delete( tree, ( caddr_t )var, rewrite_var_cmp );
+ rewrite_var_free( var );
+ var = NULL;
+ }
+
+ return var;
+}
+
+/*
+ * Sets/inserts a var
+ */
+struct rewrite_var *
+rewrite_var_set_f(
+ Avlnode **tree,
+ const char *name,
+ const char *value,
+ int flags
+)
+{
+ struct rewrite_var *var;
+
+ assert( tree != NULL );
+ assert( name != NULL );
+ assert( value != NULL );
+
+ var = rewrite_var_find( *tree, name );
+ if ( var == NULL ) {
+ if ( flags & REWRITE_VAR_INSERT ) {
+ return rewrite_var_insert_f( tree, name, value, flags );
+
+ } else {
+ return NULL;
+ }
+
+ } else {
+ assert( var->lv_value.bv_val != NULL );
+
+ (void)rewrite_var_replace( var, value, flags );
+ }
+
+ return var;
+}
+
diff --git a/libraries/librewrite/xmap.c b/libraries/librewrite/xmap.c
new file mode 100644
index 0000000..728ed9a
--- /dev/null
+++ b/libraries/librewrite/xmap.c
@@ -0,0 +1,506 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include <stdio.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#define LDAP_DEPRECATED 1
+#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 );
+
+ *currpos = NULL;
+
+ map = calloc( sizeof( struct rewrite_map ), 1 );
+ if ( map == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "rewrite_xmap_parse:"
+ " calloc failed\n" );
+ 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" );
+ if ( map->lm_name == NULL ) {
+ free( map );
+ return NULL;
+ }
+
+ assert( s[7] == '}' );
+ *currpos = s + 8;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( !xpasswd_mutex_init ) {
+ if ( ldap_pvt_thread_mutex_init( &xpasswd_mutex ) ) {
+ free( map );
+ return NULL;
+ }
+ }
+ ++xpasswd_mutex_init;
+#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 );
+ if ( filename == NULL ) {
+ free( map );
+ return NULL;
+ }
+ AC_MEMCPY( 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 );
+ if ( url == NULL ) {
+ free( map );
+ return NULL;
+ }
+ AC_MEMCPY( url, s + c, l );
+ url[ l ] = '\0';
+
+ /*
+ * Urlencodes the '%s' for ldap_url_parse
+ */
+ p = strchr( url, '%' );
+ if ( p != NULL ) {
+ AC_MEMCPY( 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 */
+ }
+
+ free( map );
+ return NULL;
+}
+
+/*
+ * 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 ) {
+#ifdef HAVE_GETPWNAM
+ 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 = LDAP_NO_SUCH_OBJECT;
+ break;
+ }
+
+#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
+ if ( pwd->pw_gecos != NULL && pwd->pw_gecos[0] != '\0' ) {
+ int l = strlen( pwd->pw_gecos );
+
+ val->bv_val = strdup( pwd->pw_gecos );
+ val->bv_len = l;
+ } else
+#endif /* HAVE_STRUCT_PASSWD_PW_GECOS */
+ {
+ 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 */
+
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+ break;
+ }
+#endif /* HAVE_GETPWNAM*/
+
+ 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 ) {
+#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;
+ }
+
+ 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[1024];
+ 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 );
+
+ } else {
+ values = ldap_get_values( ld, entry,
+ lud->lud_attrs[0] );
+ if ( values != NULL ) {
+ val->bv_val = strdup( values[ 0 ] );
+ ldap_value_free( values );
+ }
+ }
+
+ ldap_msgfree( res );
+ ldap_unbind( ld );
+
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ val->bv_len = strlen( val->bv_val );
+
+ rc = REWRITE_SUCCESS;
+ } break;
+ }
+
+rc_return:;
+ return rc;
+}
+
+int
+rewrite_xmap_destroy(
+ struct rewrite_map **pmap
+)
+{
+ struct rewrite_map *map;
+
+ assert( pmap != NULL );
+ assert( *pmap != NULL );
+
+ map = *pmap;
+
+ switch ( map->lm_type ) {
+ case REWRITE_MAP_XPWDMAP:
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ --xpasswd_mutex_init;
+ if ( !xpasswd_mutex_init ) {
+ ldap_pvt_thread_mutex_destroy( &xpasswd_mutex );
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ break;
+
+ case REWRITE_MAP_XFILEMAP:
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( map->lm_args ) {
+ fclose( ( FILE * )map->lm_args );
+ map->lm_args = NULL;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+ ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ break;
+
+ case REWRITE_MAP_XLDAPMAP:
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( map->lm_args ) {
+ ldap_free_urldesc( ( LDAPURLDesc * )map->lm_args );
+ map->lm_args = NULL;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+ ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ break;
+
+ default:
+ break;
+
+ }
+
+ free( map->lm_name );
+ free( map );
+ *pmap = NULL;
+
+ return 0;
+}
+