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