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