diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:54:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:54:12 +0000 |
commit | b527294153be3b79563c82c66102adc0004736c0 (patch) | |
tree | 9b423a224848441885190b5ea7cf0feb23510c9d /libraries/librewrite | |
parent | Initial commit. (diff) | |
download | openldap-upstream/2.6.7+dfsg.tar.xz openldap-upstream/2.6.7+dfsg.zip |
Adding upstream version 2.6.7+dfsg.upstream/2.6.7+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libraries/librewrite')
-rw-r--r-- | libraries/librewrite/Copyright | 23 | ||||
-rw-r--r-- | libraries/librewrite/Makefile.in | 37 | ||||
-rw-r--r-- | libraries/librewrite/RATIONALE | 2 | ||||
-rw-r--r-- | libraries/librewrite/config.c | 441 | ||||
-rw-r--r-- | libraries/librewrite/context.c | 474 | ||||
-rw-r--r-- | libraries/librewrite/escapemap.c | 228 | ||||
-rw-r--r-- | libraries/librewrite/info.c | 284 | ||||
-rw-r--r-- | libraries/librewrite/ldapmap.c | 457 | ||||
-rw-r--r-- | libraries/librewrite/map.c | 588 | ||||
-rw-r--r-- | libraries/librewrite/params.c | 147 | ||||
-rw-r--r-- | libraries/librewrite/parse.c | 124 | ||||
-rw-r--r-- | libraries/librewrite/rewrite-int.h | 628 | ||||
-rw-r--r-- | libraries/librewrite/rewrite-map.h | 32 | ||||
-rw-r--r-- | libraries/librewrite/rewrite.c | 195 | ||||
-rw-r--r-- | libraries/librewrite/rule.c | 510 | ||||
-rw-r--r-- | libraries/librewrite/session.c | 427 | ||||
-rw-r--r-- | libraries/librewrite/subst.c | 513 | ||||
-rw-r--r-- | libraries/librewrite/var.c | 273 | ||||
-rw-r--r-- | libraries/librewrite/xmap.c | 506 |
19 files changed, 5889 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..f40f111 --- /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 escapemap.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 escapemap.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/escapemap.c b/libraries/librewrite/escapemap.c new file mode 100644 index 0000000..9a032dc --- /dev/null +++ b/libraries/librewrite/escapemap.c @@ -0,0 +1,228 @@ +/* $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 Ondřej Kuzník for inclusion in OpenLDAP + * Software. + */ + +#include <portable.h> + +#define LDAP_DEPRECATED 1 +#include "rewrite-int.h" +#include "rewrite-map.h" + +#include <ldap_pvt.h> + +typedef int (escape_fn)( struct berval *input, struct berval *output ); + +/* + * Map configuration, a NULL-terminated list of escape_fn pointers + */ +struct escape_map_data { + escape_fn **fn; +}; + +/* + * (un)escape functions + */ + +static int +map_escape_to_filter( struct berval *input, struct berval *output ) +{ + return ldap_bv2escaped_filter_value( input, output ); +} + +static int +map_unescape_filter( struct berval *input, struct berval *output ) +{ + ber_slen_t len; + + if ( ber_dupbv( output, input ) == NULL ) { + return REWRITE_ERR; + } + + len = ldap_pvt_filter_value_unescape( output->bv_val ); + if ( len < 0 ) { + ber_memfree( output->bv_val ); + return REWRITE_ERR; + } + output->bv_len = len; + + return LDAP_SUCCESS; +} + +static int +map_escape_to_dn( struct berval *input, struct berval *output ) +{ + LDAPAVA ava = { .la_attr = BER_BVC("uid"), + .la_value = *input, + .la_flags = LDAP_AVA_STRING }, + *ava_[] = { &ava, NULL }; + LDAPRDN rdn[] = { ava_, NULL }; + LDAPDN dn = rdn; + struct berval dnstr; + char *p; + int rc; + + rc = ldap_dn2bv( dn, &dnstr, LDAP_DN_FORMAT_LDAPV3 ); + if ( rc != LDAP_SUCCESS ) { + return REWRITE_ERR; + } + + p = strchr( dnstr.bv_val, '=' ); + p++; + + output->bv_len = dnstr.bv_len - ( p - dnstr.bv_val ); + output->bv_val = malloc( output->bv_len + 1 ); + if ( output->bv_val == NULL ) { + free( dnstr.bv_val ); + return REWRITE_ERR; + } + memcpy( output->bv_val, p, output->bv_len ); + output->bv_val[output->bv_len] = '\0'; + + free( dnstr.bv_val ); + return REWRITE_SUCCESS; +} + +static int +map_unescape_dn( struct berval *input, struct berval *output ) +{ + LDAPDN dn; + struct berval fake_dn; + char *p; + int rc = REWRITE_SUCCESS; + + fake_dn.bv_len = STRLENOF("uid=") + input->bv_len; + fake_dn.bv_val = p = malloc( fake_dn.bv_len + 1 ); + if ( p == NULL ) { + return REWRITE_ERR; + } + + memcpy( p, "uid=", STRLENOF("uid=") ); + p += STRLENOF("uid="); + memcpy( p, input->bv_val, input->bv_len ); + fake_dn.bv_val[fake_dn.bv_len] = '\0'; + + if ( ldap_bv2dn( &fake_dn, &dn, LDAP_DN_FORMAT_LDAPV3 ) != LDAP_SUCCESS ) { + free( fake_dn.bv_val ); + return REWRITE_ERR; + } + if ( ber_dupbv( output, &dn[0][0]->la_value ) == NULL ) { + rc = REWRITE_ERR; + } + ldap_dnfree( dn ); + free( fake_dn.bv_val ); + return rc; +} + +/* Registered callbacks */ + +static void * +map_escape_parse( + const char *fname, + int lineno, + int argc, + char **argv +) +{ + escape_fn **fns; + int i; + + assert( fname != NULL ); + assert( argv != NULL ); + + if ( argc < 1 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] escape map needs at least one operation\n", + fname, lineno ); + return NULL; + } + + fns = calloc( sizeof(escape_fn *), argc + 1 ); + if ( fns == NULL ) { + return NULL; + } + + for ( i = 0; i < argc; i++ ) { + if ( strcasecmp( argv[i], "escape2dn" ) == 0 ) { + fns[i] = map_escape_to_dn; + } else if ( strcasecmp( argv[i], "escape2filter" ) == 0 ) { + fns[i] = map_escape_to_filter; + } else if ( strcasecmp( argv[i], "unescapedn" ) == 0 ) { + fns[i] = map_unescape_dn; + } else if ( strcasecmp( argv[i], "unescapefilter" ) == 0 ) { + fns[i] = map_unescape_filter; + } else { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unknown option %s (ignored)\n", + fname, lineno, argv[i] ); + free( fns ); + return NULL; + } + } + + return (void *)fns; +} + +static int +map_escape_apply( + void *private, + const char *input, + struct berval *output ) +{ + escape_fn **fns = private; + struct berval tmpin, tmpout = BER_BVNULL; + int i; + + assert( private != NULL ); + assert( input != NULL ); + assert( output != NULL ); + + ber_str2bv( input, 0, 1, &tmpin ); + + for ( i=0; fns[i]; i++ ) { + int rc = fns[i]( &tmpin, &tmpout ); + free( tmpin.bv_val ); + if ( rc != REWRITE_SUCCESS ) { + return rc; + } + tmpin = tmpout; + BER_BVZERO( &tmpout ); + } + *output = tmpin; + + return REWRITE_SUCCESS; +} + +static int +map_escape_destroy( + void *private +) +{ + struct ldap_map_data *data = private; + + assert( private != NULL ); + free( data ); + + return 0; +} + +const rewrite_mapper rewrite_escape_mapper = { + "escape", + map_escape_parse, + map_escape_apply, + map_escape_destroy +}; 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..e62cd3a --- /dev/null +++ b/libraries/librewrite/ldapmap.c @@ -0,0 +1,457 @@ +/* $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; + ldap_unbind_ext( ld, NULL, NULL ); + 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; + ldap_unbind_ext( ld, NULL, NULL ); + if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + set_version = 1; + ldap_msgfree( res ); + res = NULL; + goto do_bind; + + } else if ( rc != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + + if ( ldap_count_entries( ld, res ) != 1 ) { + 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 ); + } + } + + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + goto rc_return; + } + +rc_return:; + ldap_msgfree( res ); + + 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..c5243a0 --- /dev/null +++ b/libraries/librewrite/map.c @@ -0,0 +1,588 @@ +/* $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; + +/* escapemap.c */ +extern const rewrite_mapper rewrite_escape_mapper; + +const rewrite_mapper * +rewrite_mapper_find( + const char *name +) +{ + int i; + + if ( !strcasecmp( name, "ldap" )) + return &rewrite_ldap_mapper; + + if ( !strcasecmp( name, "escape" )) + return &rewrite_escape_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; +} + |