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