1 /*	$NetBSD: rwmconf.c,v 1.1.1.3 2010/12/12 15:23:43 adam Exp $	*/
2 
3 /* rwmconf.c - rewrite/map configuration file routines */
4 /* OpenLDAP: pkg/ldap/servers/slapd/overlays/rwmconf.c,v 1.25.2.7 2010/06/10 17:37:40 quanah Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1999-2010 The OpenLDAP Foundation.
8  * Portions Copyright 1999-2003 Howard Chu.
9  * Portions Copyright 2000-2003 Pierangelo Masarati.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted only as authorized by the OpenLDAP
14  * Public License.
15  *
16  * A copy of this license is available in the file LICENSE in the
17  * top-level directory of the distribution or, alternatively, at
18  * <http://www.OpenLDAP.org/license.html>.
19  */
20 /* ACKNOWLEDGEMENTS:
21  * This work was initially developed by the Howard Chu for inclusion
22  * in OpenLDAP Software and subsequently enhanced by Pierangelo
23  * Masarati.
24  */
25 
26 #include "portable.h"
27 
28 #ifdef SLAPD_OVER_RWM
29 
30 #include <stdio.h>
31 
32 #include <ac/string.h>
33 #include <ac/socket.h>
34 
35 #include "slap.h"
36 #include "rwm.h"
37 #include "lutil.h"
38 
39 int
40 rwm_map_config(
41 		struct ldapmap	*oc_map,
42 		struct ldapmap	*at_map,
43 		const char	*fname,
44 		int		lineno,
45 		int		argc,
46 		char		**argv )
47 {
48 	struct ldapmap		*map;
49 	struct ldapmapping	*mapping;
50 	char			*src, *dst;
51 	int			is_oc = 0;
52 	int			rc = 0;
53 
54 	if ( argc < 3 || argc > 4 ) {
55 		Debug( LDAP_DEBUG_ANY,
56 	"%s: line %d: syntax is \"map {objectclass | attribute} [<local> | *] {<foreign> | *}\"\n",
57 			fname, lineno, 0 );
58 		return 1;
59 	}
60 
61 	if ( strcasecmp( argv[1], "objectclass" ) == 0 ) {
62 		map = oc_map;
63 		is_oc = 1;
64 
65 	} else if ( strcasecmp( argv[1], "attribute" ) == 0 ) {
66 		map = at_map;
67 
68 	} else {
69 		Debug( LDAP_DEBUG_ANY, "%s: line %d: syntax is "
70 			"\"map {objectclass | attribute} [<local> | *] "
71 			"{<foreign> | *}\"\n",
72 			fname, lineno, 0 );
73 		return 1;
74 	}
75 
76 	if ( !is_oc && map->map == NULL ) {
77 		/* only init if required */
78 		if ( rwm_map_init( map, &mapping ) != LDAP_SUCCESS ) {
79 			return 1;
80 		}
81 	}
82 
83 	if ( strcmp( argv[2], "*" ) == 0 ) {
84 		if ( argc < 4 || strcmp( argv[3], "*" ) == 0 ) {
85 			map->drop_missing = ( argc < 4 );
86 			goto success_return;
87 		}
88 		src = dst = argv[3];
89 
90 	} else if ( argc < 4 ) {
91 		src = "";
92 		dst = argv[2];
93 
94 	} else {
95 		src = argv[2];
96 		dst = ( strcmp( argv[3], "*" ) == 0 ? src : argv[3] );
97 	}
98 
99 	if ( ( map == at_map )
100 			&& ( strcasecmp( src, "objectclass" ) == 0
101 			|| strcasecmp( dst, "objectclass" ) == 0 ) )
102 	{
103 		Debug( LDAP_DEBUG_ANY,
104 			"%s: line %d: objectclass attribute cannot be mapped\n",
105 			fname, lineno, 0 );
106 		return 1;
107 	}
108 
109 	mapping = (struct ldapmapping *)ch_calloc( 2,
110 		sizeof(struct ldapmapping) );
111 	if ( mapping == NULL ) {
112 		Debug( LDAP_DEBUG_ANY,
113 			"%s: line %d: out of memory\n",
114 			fname, lineno, 0 );
115 		return 1;
116 	}
117 	ber_str2bv( src, 0, 1, &mapping[0].m_src );
118 	ber_str2bv( dst, 0, 1, &mapping[0].m_dst );
119 	mapping[1].m_src = mapping[0].m_dst;
120 	mapping[1].m_dst = mapping[0].m_src;
121 
122 	mapping[0].m_flags = RWMMAP_F_NONE;
123 	mapping[1].m_flags = RWMMAP_F_NONE;
124 
125 	/*
126 	 * schema check
127 	 */
128 	if ( is_oc ) {
129 		if ( src[0] != '\0' ) {
130 			mapping[0].m_src_oc = oc_bvfind( &mapping[0].m_src );
131 			if ( mapping[0].m_src_oc == NULL ) {
132 				Debug( LDAP_DEBUG_ANY,
133 	"%s: line %d: warning, source objectClass '%s' "
134 	"should be defined in schema\n",
135 					fname, lineno, src );
136 
137 				/*
138 				 * FIXME: this should become an err
139 				 */
140 				mapping[0].m_src_oc = ch_malloc( sizeof( ObjectClass ) );
141 				memset( mapping[0].m_src_oc, 0, sizeof( ObjectClass ) );
142 				mapping[0].m_src_oc->soc_cname = mapping[0].m_src;
143 				mapping[0].m_flags |= RWMMAP_F_FREE_SRC;
144 			}
145 			mapping[1].m_dst_oc = mapping[0].m_src_oc;
146 		}
147 
148 		mapping[0].m_dst_oc = oc_bvfind( &mapping[0].m_dst );
149 		if ( mapping[0].m_dst_oc == NULL ) {
150 			Debug( LDAP_DEBUG_ANY,
151 	"%s: line %d: warning, destination objectClass '%s' "
152 	"is not defined in schema\n",
153 				fname, lineno, dst );
154 
155 			mapping[0].m_dst_oc = oc_bvfind_undef( &mapping[0].m_dst );
156 			if ( mapping[0].m_dst_oc == NULL ) {
157 				Debug( LDAP_DEBUG_ANY, "%s: line %d: unable to mimic destination objectClass '%s'\n",
158 					fname, lineno, dst );
159 				goto error_return;
160 			}
161 		}
162 		mapping[1].m_src_oc = mapping[0].m_dst_oc;
163 
164 		mapping[0].m_flags |= RWMMAP_F_IS_OC;
165 		mapping[1].m_flags |= RWMMAP_F_IS_OC;
166 
167 	} else {
168 		int			rc;
169 		const char		*text = NULL;
170 
171 		if ( src[0] != '\0' ) {
172 			rc = slap_bv2ad( &mapping[0].m_src,
173 					&mapping[0].m_src_ad, &text );
174 			if ( rc != LDAP_SUCCESS ) {
175 				Debug( LDAP_DEBUG_ANY,
176 	"%s: line %d: warning, source attributeType '%s' "
177 	"should be defined in schema\n",
178 					fname, lineno, src );
179 
180 				/*
181 				 * we create a fake "proxied" ad
182 				 * and add it here.
183 				 */
184 
185 				rc = slap_bv2undef_ad( &mapping[0].m_src,
186 						&mapping[0].m_src_ad, &text,
187 						SLAP_AD_PROXIED );
188 				if ( rc != LDAP_SUCCESS ) {
189 					char prefix[1024];
190 					snprintf( prefix, sizeof(prefix),
191 	"%s: line %d: source attributeType '%s': %d",
192 						fname, lineno, src, rc );
193 					Debug( LDAP_DEBUG_ANY, "%s (%s)\n",
194 						prefix, text ? text : "null", 0 );
195 					goto error_return;
196 				}
197 
198 			}
199 			mapping[1].m_dst_ad = mapping[0].m_src_ad;
200 		}
201 
202 		rc = slap_bv2ad( &mapping[0].m_dst, &mapping[0].m_dst_ad, &text );
203 		if ( rc != LDAP_SUCCESS ) {
204 			Debug( LDAP_DEBUG_ANY,
205 	"%s: line %d: warning, destination attributeType '%s' "
206 	"is not defined in schema\n",
207 				fname, lineno, dst );
208 
209 			rc = slap_bv2undef_ad( &mapping[0].m_dst,
210 					&mapping[0].m_dst_ad, &text,
211 					SLAP_AD_PROXIED );
212 			if ( rc != LDAP_SUCCESS ) {
213 				char prefix[1024];
214 				snprintf( prefix, sizeof(prefix),
215 	"%s: line %d: destination attributeType '%s': %d",
216 					fname, lineno, dst, rc );
217 				Debug( LDAP_DEBUG_ANY, "%s (%s)\n",
218 					prefix, text ? text : "null", 0 );
219 				goto error_return;
220 			}
221 		}
222 		mapping[1].m_src_ad = mapping[0].m_dst_ad;
223 	}
224 
225 	if ( ( src[0] != '\0' && avl_find( map->map, (caddr_t)mapping, rwm_mapping_cmp ) != NULL)
226 			|| avl_find( map->remap, (caddr_t)&mapping[1], rwm_mapping_cmp ) != NULL)
227 	{
228 		Debug( LDAP_DEBUG_ANY,
229 			"%s: line %d: duplicate mapping found.\n",
230 			fname, lineno, 0 );
231 		/* FIXME: free stuff */
232 		goto error_return;
233 	}
234 
235 	if ( src[0] != '\0' ) {
236 		avl_insert( &map->map, (caddr_t)&mapping[0],
237 					rwm_mapping_cmp, rwm_mapping_dup );
238 	}
239 	avl_insert( &map->remap, (caddr_t)&mapping[1],
240 				rwm_mapping_cmp, rwm_mapping_dup );
241 
242 success_return:;
243 	return rc;
244 
245 error_return:;
246 	if ( mapping ) {
247 		rwm_mapping_free( mapping );
248 	}
249 
250 	return 1;
251 }
252 
253 static char *
254 rwm_suffix_massage_regexize( const char *s )
255 {
256 	char *res, *ptr;
257 	const char *p, *r;
258 	int i;
259 
260 	if ( s[0] == '\0' ) {
261 		return ch_strdup( "^(.+)$" );
262 	}
263 
264 	for ( i = 0, p = s;
265 			( r = strchr( p, ',' ) ) != NULL;
266 			p = r + 1, i++ )
267 		;
268 
269 	res = ch_calloc( sizeof( char ), strlen( s )
270 			+ STRLENOF( "((.+),)?" )
271 			+ STRLENOF( "[ ]?" ) * i
272 			+ STRLENOF( "$" ) + 1 );
273 
274 	ptr = lutil_strcopy( res, "((.+),)?" );
275 	for ( i = 0, p = s;
276 			( r = strchr( p, ',' ) ) != NULL;
277 			p = r + 1 , i++ ) {
278 		ptr = lutil_strncopy( ptr, p, r - p + 1 );
279 		ptr = lutil_strcopy( ptr, "[ ]?" );
280 
281 		if ( r[ 1 ] == ' ' ) {
282 			r++;
283 		}
284 	}
285 	ptr = lutil_strcopy( ptr, p );
286 	ptr[0] = '$';
287 	ptr[1] = '\0';
288 
289 	return res;
290 }
291 
292 static char *
293 rwm_suffix_massage_patternize( const char *s, const char *p )
294 {
295 	ber_len_t	len;
296 	char		*res, *ptr;
297 
298 	len = strlen( p );
299 
300 	if ( s[ 0 ] == '\0' ) {
301 		len++;
302 	}
303 
304 	res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
305 	if ( res == NULL ) {
306 		return NULL;
307 	}
308 
309 	ptr = lutil_strcopy( res, ( p[0] == '\0' ? "%2" : "%1" ) );
310 	if ( s[ 0 ] == '\0' ) {
311 		ptr[ 0 ] = ',';
312 		ptr++;
313 	}
314 	lutil_strcopy( ptr, p );
315 
316 	return res;
317 }
318 
319 int
320 rwm_suffix_massage_config(
321 		struct rewrite_info *info,
322 		struct berval *pvnc,
323 		struct berval *nvnc,
324 		struct berval *prnc,
325 		struct berval *nrnc
326 )
327 {
328 	char *rargv[ 5 ];
329 	int line = 0;
330 
331 	rargv[ 0 ] = "rewriteEngine";
332 	rargv[ 1 ] = "on";
333 	rargv[ 2 ] = NULL;
334 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
335 
336 	rargv[ 0 ] = "rewriteContext";
337 	rargv[ 1 ] = "default";
338 	rargv[ 2 ] = NULL;
339 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
340 
341 	rargv[ 0 ] = "rewriteRule";
342 	rargv[ 1 ] = rwm_suffix_massage_regexize( pvnc->bv_val );
343 	rargv[ 2 ] = rwm_suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
344 	rargv[ 3 ] = ":";
345 	rargv[ 4 ] = NULL;
346 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
347 	ch_free( rargv[ 1 ] );
348 	ch_free( rargv[ 2 ] );
349 
350 	if ( BER_BVISEMPTY( pvnc ) ) {
351 		rargv[ 0 ] = "rewriteRule";
352 		rargv[ 1 ] = "^$";
353 		rargv[ 2 ] = prnc->bv_val;
354 		rargv[ 3 ] = ":";
355 		rargv[ 4 ] = NULL;
356 		rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
357 	}
358 
359 	rargv[ 0 ] = "rewriteContext";
360 	rargv[ 1 ] = "searchEntryDN";
361 	rargv[ 2 ] = NULL;
362 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
363 
364 	rargv[ 0 ] = "rewriteRule";
365 	rargv[ 1 ] = rwm_suffix_massage_regexize( prnc->bv_val );
366 	rargv[ 2 ] = rwm_suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
367 	rargv[ 3 ] = ":";
368 	rargv[ 4 ] = NULL;
369 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
370 	ch_free( rargv[ 1 ] );
371 	ch_free( rargv[ 2 ] );
372 
373 	if ( BER_BVISEMPTY( prnc ) ) {
374 		rargv[ 0 ] = "rewriteRule";
375 		rargv[ 1 ] = "^$";
376 		rargv[ 2 ] = pvnc->bv_val;
377 		rargv[ 3 ] = ":";
378 		rargv[ 4 ] = NULL;
379 		rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
380 	}
381 
382 	rargv[ 0 ] = "rewriteContext";
383 	rargv[ 1 ] = "matchedDN";
384 	rargv[ 2 ] = "alias";
385 	rargv[ 3 ] = "searchEntryDN";
386 	rargv[ 4 ] = NULL;
387 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
388 
389 #ifdef RWM_REFERRAL_REWRITE
390 	/* FIXME: we don't want this on by default, do we? */
391 	rargv[ 0 ] = "rewriteContext";
392 	rargv[ 1 ] = "referralDN";
393 	rargv[ 2 ] = "alias";
394 	rargv[ 3 ] = "searchEntryDN";
395 	rargv[ 4 ] = NULL;
396 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
397 #else /* ! RWM_REFERRAL_REWRITE */
398 	rargv[ 0 ] = "rewriteContext";
399 	rargv[ 1 ] = "referralAttrDN";
400 	rargv[ 2 ] = NULL;
401 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
402 
403 	rargv[ 0 ] = "rewriteContext";
404 	rargv[ 1 ] = "referralDN";
405 	rargv[ 2 ] = NULL;
406 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
407 #endif /* ! RWM_REFERRAL_REWRITE */
408 
409 	rargv[ 0 ] = "rewriteContext";
410 	rargv[ 1 ] = "searchAttrDN";
411 	rargv[ 2 ] = "alias";
412 	rargv[ 3 ] = "searchEntryDN";
413 	rargv[ 4 ] = NULL;
414 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
415 
416 	return 0;
417 }
418 
419 #endif /* SLAPD_OVER_RWM */
420