1 /* $NetBSD: candidates.c,v 1.3 2021/08/14 16:15:00 christos Exp $ */
2
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1999-2021 The OpenLDAP Foundation.
7 * Portions Copyright 2001-2003 Pierangelo Masarati.
8 * Portions Copyright 1999-2003 Howard Chu.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted only as authorized by the OpenLDAP
13 * Public License.
14 *
15 * A copy of this license is available in the file LICENSE in the
16 * top-level directory of the distribution or, alternatively, at
17 * <http://www.OpenLDAP.org/license.html>.
18 */
19 /* ACKNOWLEDGEMENTS:
20 * This work was initially developed by the Howard Chu for inclusion
21 * in OpenLDAP Software and subsequently enhanced by Pierangelo
22 * Masarati.
23 */
24
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: candidates.c,v 1.3 2021/08/14 16:15:00 christos Exp $");
27
28 #include "portable.h"
29
30 #include <stdio.h>
31 #include "ac/string.h"
32
33 #include "slap.h"
34 #include "../back-ldap/back-ldap.h"
35 #include "back-meta.h"
36
37 /*
38 * The meta-directory has one suffix, called <suffix>.
39 * It handles a pool of target servers, each with a branch suffix
40 * of the form <branch X>,<suffix>, where <branch X> may be empty.
41 *
42 * When the meta-directory receives a request with a request DN that belongs
43 * to a branch, the corresponding target is invoked. When the request DN
44 * does not belong to a specific branch, all the targets that
45 * are compatible with the request DN are selected as candidates, and
46 * the request is spawned to all the candidate targets
47 *
48 * A request is characterized by a request DN. The following cases are
49 * handled:
50 * - the request DN is the suffix: <dn> == <suffix>,
51 * all the targets are candidates (search ...)
52 * - the request DN is a branch suffix: <dn> == <branch X>,<suffix>, or
53 * - the request DN is a subtree of a branch suffix:
54 * <dn> == <rdn>,<branch X>,<suffix>,
55 * the target is the only candidate.
56 *
57 * A possible extension will include the handling of multiple suffixes
58 */
59
60 static metasubtree_t *
meta_subtree_match(metatarget_t * mt,struct berval * ndn,int scope)61 meta_subtree_match( metatarget_t *mt, struct berval *ndn, int scope )
62 {
63 metasubtree_t *ms = mt->mt_subtree;
64
65 for ( ms = mt->mt_subtree; ms; ms = ms->ms_next ) {
66 switch ( ms->ms_type ) {
67 case META_ST_SUBTREE:
68 if ( dnIsSuffix( ndn, &ms->ms_dn ) ) {
69 return ms;
70 }
71 break;
72
73 case META_ST_SUBORDINATE:
74 if ( dnIsSuffix( ndn, &ms->ms_dn ) &&
75 ( ndn->bv_len > ms->ms_dn.bv_len || scope != LDAP_SCOPE_BASE ) )
76 {
77 return ms;
78 }
79 break;
80
81 case META_ST_REGEX:
82 /* NOTE: cannot handle scope */
83 if ( regexec( &ms->ms_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
84 return ms;
85 }
86 break;
87 }
88 }
89
90 return NULL;
91 }
92
93 /*
94 * returns 1 if suffix is candidate for dn, otherwise 0
95 *
96 * Note: this function should never be called if dn is the <suffix>.
97 */
98 int
meta_back_is_candidate(metatarget_t * mt,struct berval * ndn,int scope)99 meta_back_is_candidate(
100 metatarget_t *mt,
101 struct berval *ndn,
102 int scope )
103 {
104 struct berval rdn;
105 int d = ndn->bv_len - mt->mt_nsuffix.bv_len;
106
107 if ( d >= 0 ) {
108 if ( !dnIsSuffix( ndn, &mt->mt_nsuffix ) ) {
109 return META_NOT_CANDIDATE;
110 }
111
112 /*
113 * | match | exclude |
114 * +---------+---------+-------------------+
115 * | T | T | not candidate |
116 * | F | T | continue checking |
117 * +---------+---------+-------------------+
118 * | T | F | candidate |
119 * | F | F | not candidate |
120 * +---------+---------+-------------------+
121 */
122
123 if ( mt->mt_subtree ) {
124 int match = ( meta_subtree_match( mt, ndn, scope ) != NULL );
125
126 if ( !mt->mt_subtree_exclude ) {
127 return match ? META_CANDIDATE : META_NOT_CANDIDATE;
128 }
129
130 if ( match /* && mt->mt_subtree_exclude */ ) {
131 return META_NOT_CANDIDATE;
132 }
133 }
134
135 switch ( mt->mt_scope ) {
136 case LDAP_SCOPE_SUBTREE:
137 default:
138 return META_CANDIDATE;
139
140 case LDAP_SCOPE_SUBORDINATE:
141 if ( d > 0 ) {
142 return META_CANDIDATE;
143 }
144 break;
145
146 /* nearly useless; not allowed by config */
147 case LDAP_SCOPE_ONELEVEL:
148 if ( d > 0 ) {
149 rdn.bv_val = ndn->bv_val;
150 rdn.bv_len = (ber_len_t)d - STRLENOF( "," );
151 if ( dnIsOneLevelRDN( &rdn ) ) {
152 return META_CANDIDATE;
153 }
154 }
155 break;
156
157 /* nearly useless; not allowed by config */
158 case LDAP_SCOPE_BASE:
159 if ( d == 0 ) {
160 return META_CANDIDATE;
161 }
162 break;
163 }
164
165 } else /* if ( d < 0 ) */ {
166 if ( !dnIsSuffix( &mt->mt_nsuffix, ndn ) ) {
167 return META_NOT_CANDIDATE;
168 }
169
170 switch ( scope ) {
171 case LDAP_SCOPE_SUBTREE:
172 case LDAP_SCOPE_SUBORDINATE:
173 /*
174 * suffix longer than dn, but common part matches
175 */
176 return META_CANDIDATE;
177
178 case LDAP_SCOPE_ONELEVEL:
179 rdn.bv_val = mt->mt_nsuffix.bv_val;
180 rdn.bv_len = (ber_len_t)(-d) - STRLENOF( "," );
181 if ( dnIsOneLevelRDN( &rdn ) ) {
182 return META_CANDIDATE;
183 }
184 break;
185 }
186 }
187
188 return META_NOT_CANDIDATE;
189 }
190
191 /*
192 * meta_back_select_unique_candidate
193 *
194 * returns the index of the candidate in case it is unique, otherwise
195 * META_TARGET_NONE if none matches, or
196 * META_TARGET_MULTIPLE if more than one matches
197 * Note: ndn MUST be normalized.
198 */
199 int
meta_back_select_unique_candidate(metainfo_t * mi,struct berval * ndn)200 meta_back_select_unique_candidate(
201 metainfo_t *mi,
202 struct berval *ndn )
203 {
204 int i, candidate = META_TARGET_NONE;
205
206 for ( i = 0; i < mi->mi_ntargets; i++ ) {
207 metatarget_t *mt = mi->mi_targets[ i ];
208
209 if ( meta_back_is_candidate( mt, ndn, LDAP_SCOPE_BASE ) ) {
210 if ( candidate == META_TARGET_NONE ) {
211 candidate = i;
212
213 } else {
214 return META_TARGET_MULTIPLE;
215 }
216 }
217 }
218
219 return candidate;
220 }
221
222 /*
223 * meta_clear_unused_candidates
224 *
225 * clears all candidates except candidate
226 */
227 int
meta_clear_unused_candidates(Operation * op,int candidate)228 meta_clear_unused_candidates(
229 Operation *op,
230 int candidate )
231 {
232 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
233 int i;
234 SlapReply *candidates = meta_back_candidates_get( op );
235
236 for ( i = 0; i < mi->mi_ntargets; ++i ) {
237 if ( i == candidate ) {
238 continue;
239 }
240 META_CANDIDATE_RESET( &candidates[ i ] );
241 }
242
243 return 0;
244 }
245
246 /*
247 * meta_clear_one_candidate
248 *
249 * clears the selected candidate
250 */
251 int
meta_clear_one_candidate(Operation * op,metaconn_t * mc,int candidate)252 meta_clear_one_candidate(
253 Operation *op,
254 metaconn_t *mc,
255 int candidate )
256 {
257 metasingleconn_t *msc = &mc->mc_conns[ candidate ];
258
259 if ( msc->msc_ld != NULL ) {
260
261 #ifdef DEBUG_205
262 Debug(LDAP_DEBUG_ANY,
263 "### %s meta_clear_one_candidate ldap_unbind_ext[%d] mc=%p ld=%p\n",
264 op ? op->o_log_prefix : "", candidate, (void *)mc,
265 (void *)msc->msc_ld );
266 #endif /* DEBUG_205 */
267
268 ldap_unbind_ext( msc->msc_ld, NULL, NULL );
269 msc->msc_ld = NULL;
270 }
271
272 if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
273 ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
274 BER_BVZERO( &msc->msc_bound_ndn );
275 }
276
277 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
278 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
279 ber_memfree_x( msc->msc_cred.bv_val, NULL );
280 BER_BVZERO( &msc->msc_cred );
281 }
282
283 msc->msc_mscflags = 0;
284
285 return 0;
286 }
287
288