1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2000-2021 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* ACKNOWLEDGEMENT:
16  * This work was initially developed by Pierangelo Masarati for
17  * inclusion in OpenLDAP Software.
18  */
19 
20 #include <portable.h>
21 
22 #include "rewrite-int.h"
23 #include "rewrite-map.h"
24 
25 /*
26  * Parses a plugin map
27  */
28 static int
29 rewrite_parse_builtin_map(
30 		struct rewrite_info *info,
31 		const char *fname,
32 		int lineno,
33 		int argc,
34 		char **argv
35 );
36 
37 /*
38  * Parses a config line and takes actions to fit content in rewrite structure;
39  * lines handled are of the form:
40  *
41  *      rewriteEngine 		{on|off}
42  *      rewriteMaxPasses        numPasses [numPassesPerRule]
43  *      rewriteContext 		contextName [alias aliasedContextName]
44  *      rewriteRule 		pattern substPattern [ruleFlags]
45  *      rewriteMap 		mapType mapName [mapArgs]
46  *      rewriteParam		paramName paramValue
47  */
48 int
rewrite_parse(struct rewrite_info * info,const char * fname,int lineno,int argc,char ** argv)49 rewrite_parse(
50 		struct rewrite_info *info,
51 		const char *fname,
52 		int lineno,
53 		int argc,
54 		char **argv
55 )
56 {
57 	int rc = -1;
58 
59 	assert( info != NULL );
60 	assert( fname != NULL );
61 	assert( argv != NULL );
62 	assert( argc > 0 );
63 
64 	/*
65 	 * Switch on the rewrite engine
66 	 */
67 	if ( strcasecmp( argv[ 0 ], "rewriteEngine" ) == 0 ) {
68 		if ( argc < 2 ) {
69 			Debug( LDAP_DEBUG_ANY,
70 					"[%s:%d] rewriteEngine needs 'state'\n",
71 					fname, lineno );
72 			return -1;
73 
74 		} else if ( argc > 2 ) {
75 			Debug( LDAP_DEBUG_ANY,
76 					"[%s:%d] extra fields in rewriteEngine"
77 					" will be discarded\n",
78 					fname, lineno );
79 		}
80 
81 		if ( strcasecmp( argv[ 1 ], "on" ) == 0 ) {
82 			info->li_state = REWRITE_ON;
83 
84 		} else if ( strcasecmp( argv[ 1 ], "off" ) == 0 ) {
85 			info->li_state = REWRITE_OFF;
86 
87 		} else {
88 			Debug( LDAP_DEBUG_ANY,
89 					"[%s:%d] unknown 'state' in rewriteEngine;"
90 					" assuming 'on'\n",
91 					fname, lineno );
92 			info->li_state = REWRITE_ON;
93 		}
94 		rc = REWRITE_SUCCESS;
95 
96 	/*
97 	 * Alter max passes
98 	 */
99 	} else if ( strcasecmp( argv[ 0 ], "rewriteMaxPasses" ) == 0 ) {
100 		if ( argc < 2 ) {
101 			Debug( LDAP_DEBUG_ANY,
102 					"[%s:%d] rewriteMaxPasses needs 'value'\n",
103 					fname, lineno );
104 			return -1;
105 		}
106 
107 		if ( lutil_atoi( &info->li_max_passes, argv[ 1 ] ) != 0 ) {
108 			Debug( LDAP_DEBUG_ANY,
109 					"[%s:%d] unable to parse rewriteMaxPasses=\"%s\"\n",
110 					fname, lineno, argv[ 1 ] );
111 			return -1;
112 		}
113 
114 		if ( info->li_max_passes <= 0 ) {
115 			Debug( LDAP_DEBUG_ANY,
116 					"[%s:%d] negative or null rewriteMaxPasses\n",
117 					fname, lineno );
118 			return -1;
119 		}
120 
121 		if ( argc > 2 ) {
122 			if ( lutil_atoi( &info->li_max_passes_per_rule, argv[ 2 ] ) != 0 ) {
123 				Debug( LDAP_DEBUG_ANY,
124 						"[%s:%d] unable to parse rewriteMaxPassesPerRule=\"%s\"\n",
125 						fname, lineno, argv[ 2 ] );
126 				return -1;
127 			}
128 
129 			if ( info->li_max_passes_per_rule <= 0 ) {
130 				Debug( LDAP_DEBUG_ANY,
131 						"[%s:%d] negative or null rewriteMaxPassesPerRule\n",
132 						fname, lineno );
133 				return -1;
134 			}
135 
136 		} else {
137 			info->li_max_passes_per_rule = info->li_max_passes;
138 		}
139 		rc = REWRITE_SUCCESS;
140 
141 	/*
142 	 * Start a new rewrite context and set current context
143 	 */
144 	} else if ( strcasecmp( argv[ 0 ], "rewriteContext" ) == 0 ) {
145 		if ( argc < 2 ) {
146 			Debug( LDAP_DEBUG_ANY,
147 					"[%s:%d] rewriteContext needs 'name'\n",
148 					fname, lineno );
149 			return -1;
150 		}
151 
152 		/*
153 		 * Checks for existence (lots of contexts should be
154 		 * available by default ...)
155 		 */
156 		 rewrite_int_curr_context = rewrite_context_find( info, argv[ 1 ] );
157 		 if ( rewrite_int_curr_context == NULL ) {
158 			 rewrite_int_curr_context = rewrite_context_create( info,
159 					 argv[ 1 ] );
160 		 }
161 		 if ( rewrite_int_curr_context == NULL ) {
162 			 return -1;
163 		 }
164 
165 		 if ( argc > 2 ) {
166 
167 			 /*
168 			  * A context can alias another (e.g., the `builtin'
169 			  * contexts for backend operations, if not defined,
170 			  * alias the `default' rewrite context (with the
171 			  * notable exception of the searchResult context,
172 			  * which can be undefined)
173 			  */
174 			 if ( strcasecmp( argv[ 2 ], "alias" ) == 0 ) {
175 				 struct rewrite_context *aliased;
176 
177 				 if ( argc == 3 ) {
178 					 Debug( LDAP_DEBUG_ANY,
179 							 "[%s:%d] rewriteContext"
180 							 " needs 'name' after"
181 							 " 'alias'\n",
182 							 fname, lineno );
183 					 return -1;
184 
185 				 } else if ( argc > 4 ) {
186 					 Debug( LDAP_DEBUG_ANY,
187 							 "[%s:%d] extra fields in"
188 							 " rewriteContext"
189 							 " after aliased name"
190 							 " will be"
191 							 " discarded\n",
192 							 fname, lineno );
193 				 }
194 
195 				 aliased = rewrite_context_find( info,
196 						 argv[ 3 ] );
197 				 if ( aliased == NULL ) {
198 					 Debug( LDAP_DEBUG_ANY,
199 							 "[%s:%d] aliased"
200 							 " rewriteContext '%s'"
201 							 " does not exists\n",
202 							 fname, lineno,
203 							 argv[ 3 ] );
204 					 return -1;
205 				 }
206 
207 				 rewrite_int_curr_context->lc_alias = aliased;
208 				 rewrite_int_curr_context = aliased;
209 
210 			 } else {
211 				 Debug( LDAP_DEBUG_ANY,
212 						 "[%s:%d] extra fields"
213 						 " in rewriteContext"
214 						 " will be discarded\n",
215 						 fname, lineno );
216 			 }
217 		 }
218 		 rc = REWRITE_SUCCESS;
219 
220 	/*
221 	 * Compile a rule in current context
222 	 */
223 	} else if ( strcasecmp( argv[ 0 ], "rewriteRule" ) == 0 ) {
224 		if ( argc < 3 ) {
225 			Debug( LDAP_DEBUG_ANY,
226 					"[%s:%d] rewriteRule needs 'pattern'"
227 					" 'subst' ['flags']\n",
228 					fname, lineno );
229 			return -1;
230 
231 		} else if ( argc > 4 ) {
232 			Debug( LDAP_DEBUG_ANY,
233 					"[%s:%d] extra fields in rewriteRule"
234 					" will be discarded\n",
235 					fname, lineno );
236 		}
237 
238 		if ( rewrite_int_curr_context == NULL ) {
239 			Debug( LDAP_DEBUG_ANY,
240 					"[%s:%d] rewriteRule outside a"
241 					" context; will add to default\n",
242 					fname, lineno );
243 			rewrite_int_curr_context = rewrite_context_find( info,
244 					REWRITE_DEFAULT_CONTEXT );
245 
246 			/*
247 			 * Default context MUST exist in a properly initialized
248 			 * struct rewrite_info
249 			 */
250 			assert( rewrite_int_curr_context != NULL );
251 		}
252 
253 		rc = rewrite_rule_compile( info, rewrite_int_curr_context, argv[ 1 ],
254 				argv[ 2 ], ( argc == 4 ? argv[ 3 ] : "" ) );
255 
256 	/*
257 	 * Add a plugin map to the map tree
258 	 */
259 	} else if ( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 ) {
260 		if ( argc < 3 ) {
261 			Debug( LDAP_DEBUG_ANY,
262 					"[%s:%d] rewriteMap needs at least 'type'"
263 					" and 'name' ['args']\n",
264 					fname, lineno );
265 			return -1;
266 		}
267 
268 		rc = rewrite_parse_builtin_map( info, fname, lineno,
269 				argc, argv );
270 
271 	/*
272 	 * Set the value of a global scope parameter
273 	 */
274 	} else if ( strcasecmp( argv[ 0 ], "rewriteParam" ) == 0 ) {
275 		if ( argc < 3 ) {
276 			Debug( LDAP_DEBUG_ANY,
277 					"[%s:%d] rewriteParam needs 'name'"
278 					" and 'value'\n",
279 					fname, lineno );
280 			return -1;
281 		}
282 
283 		rc = rewrite_param_set( info, argv[ 1 ], argv[ 2 ] );
284 
285 	/*
286 	 * Error
287 	 */
288 	} else {
289 		Debug( LDAP_DEBUG_ANY,
290 				"[%s:%d] unknown command '%s'\n",
291 				fname, lineno, argv[ 0 ] );
292 		return -1;
293 	}
294 
295 	return rc;
296 }
297 
298 /*
299  * Compares two maps
300  */
301 static int
rewrite_builtin_map_cmp(const void * c1,const void * c2)302 rewrite_builtin_map_cmp(
303 		const void *c1,
304                 const void *c2
305 )
306 {
307 	const struct rewrite_builtin_map *m1, *m2;
308 
309         m1 = ( const struct rewrite_builtin_map * )c1;
310         m2 = ( const struct rewrite_builtin_map * )c2;
311 
312         assert( m1 != NULL );
313         assert( m2 != NULL );
314         assert( m1->lb_name != NULL );
315         assert( m2->lb_name != NULL );
316 
317         return strcasecmp( m1->lb_name, m2->lb_name );
318 }
319 
320 /*
321  * Duplicate map ?
322  */
323 static int
rewrite_builtin_map_dup(void * c1,void * c2)324 rewrite_builtin_map_dup(
325 	                void *c1,
326 	                void *c2
327 )
328 {
329         struct rewrite_builtin_map *m1, *m2;
330 
331         m1 = ( struct rewrite_builtin_map * )c1;
332         m2 = ( struct rewrite_builtin_map * )c2;
333 
334         assert( m1 != NULL );
335         assert( m2 != NULL );
336         assert( m1->lb_name != NULL );
337         assert( m2->lb_name != NULL );
338 
339         return ( strcasecmp( m1->lb_name, m2->lb_name ) == 0 ? -1 : 0 );
340 }
341 
342 /*
343  * Adds a map to the info map tree
344  */
345 static int
rewrite_builtin_map_insert(struct rewrite_info * info,struct rewrite_builtin_map * map)346 rewrite_builtin_map_insert(
347 		struct rewrite_info *info,
348 		struct rewrite_builtin_map *map
349 )
350 {
351 	/*
352 	 * May need a mutex?
353 	 */
354 	return ldap_avl_insert( &info->li_maps, ( caddr_t )map,
355 			rewrite_builtin_map_cmp,
356 		       	rewrite_builtin_map_dup );
357 }
358 
359 /*
360  * Retrieves a map
361  */
362 struct rewrite_builtin_map *
rewrite_builtin_map_find(struct rewrite_info * info,const char * name)363 rewrite_builtin_map_find(
364 		struct rewrite_info *info,
365 		const char *name
366 )
367 {
368 	struct rewrite_builtin_map tmp;
369 
370 	assert( info != NULL );
371 	assert( name != NULL );
372 
373 	tmp.lb_name = ( char * )name;
374 
375 	return ( struct rewrite_builtin_map * )ldap_avl_find( info->li_maps,
376 			( caddr_t )&tmp, rewrite_builtin_map_cmp );
377 }
378 
379 /*
380  * Parses a plugin map
381  */
382 static int
rewrite_parse_builtin_map(struct rewrite_info * info,const char * fname,int lineno,int argc,char ** argv)383 rewrite_parse_builtin_map(
384 		struct rewrite_info *info,
385 		const char *fname,
386 		int lineno,
387 		int argc,
388 		char **argv
389 )
390 {
391 	struct rewrite_builtin_map *map;
392 
393 #define MAP_TYPE	1
394 #define MAP_NAME	2
395 
396 	assert( info != NULL );
397 	assert( fname != NULL );
398 	assert( argc > 2 );
399 	assert( argv != NULL );
400 	assert( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 );
401 
402 	map = calloc( sizeof( struct rewrite_builtin_map ), 1 );
403 	if ( map == NULL ) {
404 		return REWRITE_ERR;
405 	}
406 
407 	map->lb_name = strdup( argv[ MAP_NAME ] );
408 	if ( map->lb_name == NULL ) {
409 		free( map );
410 		return REWRITE_ERR;
411 	}
412 
413 	/*
414 	 * Built-in ldap map
415 	 */
416 	if (( map->lb_mapper = rewrite_mapper_find( argv[ MAP_TYPE ] ))) {
417 		map->lb_type = REWRITE_BUILTIN_MAP;
418 
419 #ifdef USE_REWRITE_LDAP_PVT_THREADS
420 		if ( ldap_pvt_thread_mutex_init( & map->lb_mutex ) ) {
421 			free( map->lb_name );
422 			free( map );
423 			return REWRITE_ERR;
424 		}
425 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
426 
427 		map->lb_private = map->lb_mapper->rm_config( fname, lineno,
428 				argc - 3, argv + 3 );
429 
430 	/*
431 	 * Error
432 	 */
433 	} else {
434 		free( map );
435 		Debug( LDAP_DEBUG_ANY, "[%s:%d] unknown map type\n",
436 				fname, lineno );
437 		return -1;
438 	}
439 
440 	return rewrite_builtin_map_insert( info, map );
441 }
442