1 /* mr.c - routines to manage matching rule definitions */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2021 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 
17 #include "portable.h"
18 
19 #include <stdio.h>
20 
21 #include <ac/ctype.h>
22 #include <ac/string.h>
23 #include <ac/socket.h>
24 
25 #include "slap.h"
26 
27 struct mindexrec {
28 	struct berval	mir_name;
29 	MatchingRule	*mir_mr;
30 };
31 
32 static Avlnode	*mr_index = NULL;
33 static LDAP_SLIST_HEAD(MRList, MatchingRule) mr_list
34 	= LDAP_SLIST_HEAD_INITIALIZER(&mr_list);
35 static LDAP_SLIST_HEAD(MRUList, MatchingRuleUse) mru_list
36 	= LDAP_SLIST_HEAD_INITIALIZER(&mru_list);
37 
38 static int
mr_index_cmp(const void * v_mir1,const void * v_mir2)39 mr_index_cmp(
40     const void	*v_mir1,
41     const void	*v_mir2
42 )
43 {
44 	const struct mindexrec	*mir1 = v_mir1;
45 	const struct mindexrec	*mir2 = v_mir2;
46 	int i = mir1->mir_name.bv_len - mir2->mir_name.bv_len;
47 	if (i) return i;
48 	return (strcasecmp( mir1->mir_name.bv_val, mir2->mir_name.bv_val ));
49 }
50 
51 static int
mr_index_name_cmp(const void * v_name,const void * v_mir)52 mr_index_name_cmp(
53     const void	*v_name,
54     const void	*v_mir
55 )
56 {
57 	const struct berval    *name = v_name;
58 	const struct mindexrec *mir  = v_mir;
59 	int i = name->bv_len - mir->mir_name.bv_len;
60 	if (i) return i;
61 	return (strncasecmp( name->bv_val, mir->mir_name.bv_val, name->bv_len ));
62 }
63 
64 MatchingRule *
mr_find(const char * mrname)65 mr_find( const char *mrname )
66 {
67 	struct berval bv;
68 
69 	bv.bv_val = (char *)mrname;
70 	bv.bv_len = strlen( mrname );
71 	return mr_bvfind( &bv );
72 }
73 
74 MatchingRule *
mr_bvfind(struct berval * mrname)75 mr_bvfind( struct berval *mrname )
76 {
77 	struct mindexrec	*mir = NULL;
78 
79 	if ( (mir = ldap_avl_find( mr_index, mrname, mr_index_name_cmp )) != NULL ) {
80 		return( mir->mir_mr );
81 	}
82 	return( NULL );
83 }
84 
85 void
mr_destroy(void)86 mr_destroy( void )
87 {
88 	MatchingRule *m;
89 
90 	ldap_avl_free(mr_index, ldap_memfree);
91 	while( !LDAP_SLIST_EMPTY(&mr_list) ) {
92 		m = LDAP_SLIST_FIRST(&mr_list);
93 		LDAP_SLIST_REMOVE_HEAD(&mr_list, smr_next);
94 		ch_free( m->smr_str.bv_val );
95 		ch_free( m->smr_compat_syntaxes );
96 		ldap_matchingrule_free((LDAPMatchingRule *)m);
97 	}
98 }
99 
100 static int
mr_insert(MatchingRule * smr,const char ** err)101 mr_insert(
102     MatchingRule	*smr,
103     const char		**err
104 )
105 {
106 	struct mindexrec	*mir;
107 	char			**names;
108 
109 	LDAP_SLIST_NEXT( smr, smr_next ) = NULL;
110 	LDAP_SLIST_INSERT_HEAD(&mr_list, smr, smr_next);
111 
112 	if ( smr->smr_oid ) {
113 		mir = (struct mindexrec *)
114 			ch_calloc( 1, sizeof(struct mindexrec) );
115 		mir->mir_name.bv_val = smr->smr_oid;
116 		mir->mir_name.bv_len = strlen( smr->smr_oid );
117 		mir->mir_mr = smr;
118 		if ( ldap_avl_insert( &mr_index, (caddr_t) mir,
119 		                 mr_index_cmp, ldap_avl_dup_error ) ) {
120 			*err = smr->smr_oid;
121 			ldap_memfree(mir);
122 			return SLAP_SCHERR_MR_DUP;
123 		}
124 		/* FIX: temporal consistency check */
125 		mr_bvfind(&mir->mir_name);
126 	}
127 	if ( (names = smr->smr_names) ) {
128 		while ( *names ) {
129 			mir = (struct mindexrec *)
130 				ch_calloc( 1, sizeof(struct mindexrec) );
131 			mir->mir_name.bv_val = *names;
132 			mir->mir_name.bv_len = strlen( *names );
133 			mir->mir_mr = smr;
134 			if ( ldap_avl_insert( &mr_index, (caddr_t) mir,
135 			                 mr_index_cmp, ldap_avl_dup_error ) ) {
136 				*err = *names;
137 				ldap_memfree(mir);
138 				return SLAP_SCHERR_MR_DUP;
139 			}
140 			/* FIX: temporal consistency check */
141 			mr_bvfind(&mir->mir_name);
142 			names++;
143 		}
144 	}
145 	return 0;
146 }
147 
148 int
mr_make_syntax_compat_with_mr(Syntax * syn,MatchingRule * mr)149 mr_make_syntax_compat_with_mr(
150 	Syntax		*syn,
151 	MatchingRule	*mr )
152 {
153 	int		n = 0;
154 
155 	assert( syn != NULL );
156 	assert( mr != NULL );
157 
158 	if ( mr->smr_compat_syntaxes ) {
159 		/* count existing */
160 		for ( n = 0;
161 			mr->smr_compat_syntaxes[ n ];
162 			n++ )
163 		{
164 			if ( mr->smr_compat_syntaxes[ n ] == syn ) {
165 				/* already compatible; mmmmh... */
166 				return 1;
167 			}
168 		}
169 	}
170 
171 	mr->smr_compat_syntaxes = ch_realloc(
172 		mr->smr_compat_syntaxes,
173 		sizeof( Syntax * )*(n + 2) );
174 	mr->smr_compat_syntaxes[ n ] = syn;
175 	mr->smr_compat_syntaxes[ n + 1 ] = NULL;
176 
177 	return 0;
178 }
179 
180 int
mr_make_syntax_compat_with_mrs(const char * syntax,char * const * mrs)181 mr_make_syntax_compat_with_mrs(
182 	const char *syntax,
183 	char *const *mrs )
184 {
185 	int	r, rc = 0;
186 	Syntax	*syn;
187 
188 	assert( syntax != NULL );
189 	assert( mrs != NULL );
190 
191 	syn = syn_find( syntax );
192 	if ( syn == NULL ) {
193 		return -1;
194 	}
195 
196 	for ( r = 0; mrs[ r ] != NULL; r++ ) {
197 		MatchingRule	*mr = mr_find( mrs[ r ] );
198 		if ( mr == NULL ) {
199 			/* matchingRule not found -- ignore by now */
200 			continue;
201 		}
202 
203 		rc += mr_make_syntax_compat_with_mr( syn, mr );
204 	}
205 
206 	return rc;
207 }
208 
209 int
mr_add(LDAPMatchingRule * mr,slap_mrule_defs_rec * def,MatchingRule * amr,const char ** err)210 mr_add(
211     LDAPMatchingRule		*mr,
212     slap_mrule_defs_rec	*def,
213 	MatchingRule	*amr,
214     const char		**err
215 )
216 {
217 	MatchingRule	*smr;
218 	Syntax		*syn;
219 	Syntax		**compat_syn = NULL;
220 	int		code;
221 
222 	if( def->mrd_compat_syntaxes ) {
223 		int i;
224 		for( i=0; def->mrd_compat_syntaxes[i]; i++ ) {
225 			/* just count em */
226 		}
227 
228 		compat_syn = ch_malloc( sizeof(Syntax *) * (i+1) );
229 
230 		for( i=0; def->mrd_compat_syntaxes[i]; i++ ) {
231 			compat_syn[i] = syn_find( def->mrd_compat_syntaxes[i] );
232 			if( compat_syn[i] == NULL ) {
233 				ch_free( compat_syn );
234 				return SLAP_SCHERR_SYN_NOT_FOUND;
235 			}
236 		}
237 
238 		compat_syn[i] = NULL;
239 	}
240 
241 	smr = (MatchingRule *) ch_calloc( 1, sizeof(MatchingRule) );
242 	AC_MEMCPY( &smr->smr_mrule, mr, sizeof(LDAPMatchingRule));
243 
244 	/*
245 	 * note: smr_bvoid uses the same memory of smr_mrule.mr_oid;
246 	 * smr_oidlen is #defined as smr_bvoid.bv_len
247 	 */
248 	smr->smr_bvoid.bv_val = smr->smr_mrule.mr_oid;
249 	smr->smr_oidlen = strlen( mr->mr_oid );
250 	smr->smr_usage = def->mrd_usage;
251 	smr->smr_compat_syntaxes = compat_syn;
252 	smr->smr_normalize = def->mrd_normalize;
253 	smr->smr_match = def->mrd_match;
254 	smr->smr_indexer = def->mrd_indexer;
255 	smr->smr_filter = def->mrd_filter;
256 	smr->smr_associated = amr;
257 
258 	if ( smr->smr_syntax_oid ) {
259 		if ( (syn = syn_find(smr->smr_syntax_oid)) ) {
260 			smr->smr_syntax = syn;
261 		} else {
262 			*err = smr->smr_syntax_oid;
263 			ch_free( smr );
264 			return SLAP_SCHERR_SYN_NOT_FOUND;
265 		}
266 	} else {
267 		*err = "";
268 		ch_free( smr );
269 		return SLAP_SCHERR_MR_INCOMPLETE;
270 	}
271 	code = mr_insert(smr,err);
272 	return code;
273 }
274 
275 int
register_matching_rule(slap_mrule_defs_rec * def)276 register_matching_rule(
277 	slap_mrule_defs_rec *def )
278 {
279 	LDAPMatchingRule *mr;
280 	MatchingRule *amr = NULL;
281 	int		code;
282 	const char	*err;
283 
284 	if( def->mrd_usage == SLAP_MR_NONE && def->mrd_compat_syntaxes == NULL ) {
285 		Debug( LDAP_DEBUG_ANY, "register_matching_rule: not usable %s\n",
286 		    def->mrd_desc );
287 
288 		return -1;
289 	}
290 
291 	if( def->mrd_associated != NULL ) {
292 		amr = mr_find( def->mrd_associated );
293 		if( amr == NULL ) {
294 			Debug( LDAP_DEBUG_ANY, "register_matching_rule: "
295 				"could not locate associated matching rule %s for %s\n",
296 				def->mrd_associated, def->mrd_desc );
297 
298 			return -1;
299 		}
300 
301 		if (( def->mrd_usage & SLAP_MR_EQUALITY ) &&
302 			(( def->mrd_usage & SLAP_MR_SUBTYPE_MASK ) == SLAP_MR_NONE ))
303 		{
304 			if (( def->mrd_usage & SLAP_MR_EQUALITY ) &&
305 				(( def->mrd_usage & SLAP_MR_SUBTYPE_MASK ) != SLAP_MR_NONE ))
306 			{
307 				Debug( LDAP_DEBUG_ANY, "register_matching_rule: "
308 						"inappropriate (approx) association %s for %s\n",
309 					def->mrd_associated, def->mrd_desc );
310 				return -1;
311 			}
312 
313 		} else if (!( amr->smr_usage & SLAP_MR_EQUALITY )) {
314 				Debug( LDAP_DEBUG_ANY, "register_matching_rule: "
315 					"inappropriate (equalilty) association %s for %s\n",
316 					def->mrd_associated, def->mrd_desc );
317 				return -1;
318 		}
319 	}
320 
321 	mr = ldap_str2matchingrule( def->mrd_desc, &code, &err,
322 		LDAP_SCHEMA_ALLOW_ALL );
323 	if ( !mr ) {
324 		Debug( LDAP_DEBUG_ANY,
325 			"Error in register_matching_rule: %s before %s in %s\n",
326 		    ldap_scherr2str(code), err, def->mrd_desc );
327 
328 		return -1;
329 	}
330 
331 
332 	code = mr_add( mr, def, amr, &err );
333 
334 	ldap_memfree( mr );
335 
336 	if ( code ) {
337 		Debug( LDAP_DEBUG_ANY,
338 			"Error in register_matching_rule: %s for %s in %s\n",
339 		    scherr2str(code), err, def->mrd_desc );
340 
341 		return -1;
342 	}
343 
344 	return 0;
345 }
346 
347 void
mru_destroy(void)348 mru_destroy( void )
349 {
350 	MatchingRuleUse *m;
351 
352 	while( !LDAP_SLIST_EMPTY(&mru_list) ) {
353 		m = LDAP_SLIST_FIRST(&mru_list);
354 		LDAP_SLIST_REMOVE_HEAD(&mru_list, smru_next);
355 
356 		if ( m->smru_str.bv_val ) {
357 			ch_free( m->smru_str.bv_val );
358 			m->smru_str.bv_val = NULL;
359 		}
360 		/* memory borrowed from m->smru_mr */
361 		m->smru_oid = NULL;
362 		m->smru_names = NULL;
363 		m->smru_desc = NULL;
364 
365 		/* free what's left (basically smru_mruleuse.mru_applies_oids) */
366 		ldap_matchingruleuse_free((LDAPMatchingRuleUse *)m);
367 	}
368 }
369 
370 int
matching_rule_use_init(void)371 matching_rule_use_init( void )
372 {
373 	MatchingRule	*mr;
374 	MatchingRuleUse	**mru_ptr = &LDAP_SLIST_FIRST(&mru_list);
375 
376 	Debug( LDAP_DEBUG_TRACE, "matching_rule_use_init\n" );
377 
378 	LDAP_SLIST_FOREACH( mr, &mr_list, smr_next ) {
379 		AttributeType	*at;
380 		MatchingRuleUse	mru_storage = {{ 0 }},
381 				*mru = &mru_storage;
382 
383 		char		**applies_oids = NULL;
384 
385 		mr->smr_mru = NULL;
386 
387 		/* hide rules marked as HIDE */
388 		if ( mr->smr_usage & SLAP_MR_HIDE ) {
389 			continue;
390 		}
391 
392 		/* hide rules not marked as designed for extensibility */
393 		/* MR_EXT means can be used any attribute type whose
394 		 * syntax is same as the assertion syntax.
395 		 * Another mechanism is needed where rule can be used
396 		 * with attribute of other syntaxes.
397 		 * Framework doesn't support this (yet).
398 		 */
399 
400 		if (!( ( mr->smr_usage & SLAP_MR_EXT )
401 			|| mr->smr_compat_syntaxes ) )
402 		{
403 			continue;
404 		}
405 
406 		/*
407 		 * Note: we're using the same values of the corresponding
408 		 * MatchingRule structure; maybe we'd copy them ...
409 		 */
410 		mru->smru_mr = mr;
411 		mru->smru_obsolete = mr->smr_obsolete;
412 		mru->smru_applies_oids = NULL;
413 		LDAP_SLIST_NEXT(mru, smru_next) = NULL;
414 		mru->smru_oid = mr->smr_oid;
415 		mru->smru_names = mr->smr_names;
416 		mru->smru_desc = mr->smr_desc;
417 
418 		Debug( LDAP_DEBUG_TRACE, "    %s (%s):\n",
419 				mru->smru_oid,
420 				mru->smru_names ? mru->smru_names[ 0 ] : "" );
421 
422 		at = NULL;
423 		for ( at_start( &at ); at; at_next( &at ) ) {
424 			if( at->sat_flags & SLAP_AT_HIDE ) continue;
425 
426 			if( mr_usable_with_at( mr, at )) {
427 				ldap_charray_add( &applies_oids, at->sat_cname.bv_val );
428 			}
429 		}
430 
431 		/*
432 		 * Note: the matchingRules that are not used
433 		 * by any attributeType are not listed as
434 		 * matchingRuleUse
435 		 */
436 		if ( applies_oids != NULL ) {
437 			mru->smru_applies_oids = applies_oids;
438 			{
439 				char *str = ldap_matchingruleuse2str( &mru->smru_mruleuse );
440 				Debug( LDAP_DEBUG_TRACE, "       matchingRuleUse: %s\n", str );
441 				ldap_memfree( str );
442 			}
443 
444 			mru = (MatchingRuleUse *)ber_memalloc( sizeof( MatchingRuleUse ) );
445 			/* call-forward from MatchingRule to MatchingRuleUse */
446 			mr->smr_mru = mru;
447 			/* copy static data to newly allocated struct */
448 			*mru = mru_storage;
449 			/* append the struct pointer to the end of the list */
450 			*mru_ptr = mru;
451 			/* update the list head pointer */
452 			mru_ptr = &LDAP_SLIST_NEXT(mru,smru_next);
453 		}
454 	}
455 
456 	return( 0 );
457 }
458 
459 int
mr_usable_with_at(MatchingRule * mr,AttributeType * at)460 mr_usable_with_at(
461 	MatchingRule	*mr,
462 	AttributeType	*at )
463 {
464 	if ( ( mr->smr_usage & SLAP_MR_EXT ) && (
465 		mr->smr_syntax == at->sat_syntax ||
466 		mr == at->sat_equality ||
467 		mr == at->sat_approx ||
468 		syn_is_sup( at->sat_syntax, mr->smr_syntax ) ) )
469 	{
470 		return 1;
471 	}
472 
473 	if ( mr->smr_compat_syntaxes ) {
474 		int i;
475 		for( i=0; mr->smr_compat_syntaxes[i]; i++ ) {
476 			if( at->sat_syntax == mr->smr_compat_syntaxes[i] ) {
477 				return 1;
478 			}
479 		}
480 	}
481 	return 0;
482 }
483 
mr_schema_info(Entry * e)484 int mr_schema_info( Entry *e )
485 {
486 	AttributeDescription *ad_matchingRules = slap_schema.si_ad_matchingRules;
487 	MatchingRule *mr;
488 	struct berval nval;
489 
490 	LDAP_SLIST_FOREACH(mr, &mr_list, smr_next ) {
491 		if ( mr->smr_usage & SLAP_MR_HIDE ) {
492 			/* skip hidden rules */
493 			continue;
494 		}
495 
496 		if ( ! mr->smr_match ) {
497 			/* skip rules without matching functions */
498 			continue;
499 		}
500 
501 		if ( mr->smr_str.bv_val == NULL ) {
502 			if ( ldap_matchingrule2bv( &mr->smr_mrule, &mr->smr_str ) == NULL ) {
503 				return -1;
504 			}
505 		}
506 #if 0
507 		Debug( LDAP_DEBUG_TRACE, "Merging mr [%lu] %s\n",
508 			mr->smr_str.bv_len, mr->smr_str.bv_val );
509 #endif
510 
511 		nval.bv_val = mr->smr_oid;
512 		nval.bv_len = strlen(mr->smr_oid);
513 		if( attr_merge_one( e, ad_matchingRules, &mr->smr_str, &nval ) ) {
514 			return -1;
515 		}
516 	}
517 	return 0;
518 }
519 
mru_schema_info(Entry * e)520 int mru_schema_info( Entry *e )
521 {
522 	AttributeDescription *ad_matchingRuleUse
523 		= slap_schema.si_ad_matchingRuleUse;
524 	MatchingRuleUse	*mru;
525 	struct berval nval;
526 
527 	LDAP_SLIST_FOREACH( mru, &mru_list, smru_next ) {
528 		assert( !( mru->smru_usage & SLAP_MR_HIDE ) );
529 
530 		if ( mru->smru_str.bv_val == NULL ) {
531 			if ( ldap_matchingruleuse2bv( &mru->smru_mruleuse, &mru->smru_str )
532 					== NULL ) {
533 				return -1;
534 			}
535 		}
536 
537 #if 0
538 		Debug( LDAP_DEBUG_TRACE, "Merging mru [%lu] %s\n",
539 			mru->smru_str.bv_len, mru->smru_str.bv_val );
540 #endif
541 
542 		nval.bv_val = mru->smru_oid;
543 		nval.bv_len = strlen(mru->smru_oid);
544 		if( attr_merge_one( e, ad_matchingRuleUse, &mru->smru_str, &nval ) ) {
545 			return -1;
546 		}
547 	}
548 	return 0;
549 }
550