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