1 /* $NetBSD: search.c,v 1.4 2025/09/05 21:16:25 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2024 The OpenLDAP Foundation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17 /* Portions Copyright (c) 1995 Regents of the University of Michigan. 18 * All rights reserved. 19 * 20 * Redistribution and use in source and binary forms are permitted 21 * provided that this notice is preserved and that due credit is given 22 * to the University of Michigan at Ann Arbor. The name of the University 23 * may not be used to endorse or promote products derived from this 24 * software without specific prior written permission. This software 25 * is provided ``as is'' without express or implied warranty. 26 */ 27 28 #include <sys/cdefs.h> 29 __RCSID("$NetBSD: search.c,v 1.4 2025/09/05 21:16:25 christos Exp $"); 30 31 #include "portable.h" 32 33 #include <stdio.h> 34 35 #include <ac/string.h> 36 #include <ac/socket.h> 37 38 #include "lutil.h" 39 #include "slap.h" 40 41 int 42 do_search( 43 Operation *op, /* info about the op to which we're responding */ 44 SlapReply *rs /* all the response data we'll send */ ) 45 { 46 struct berval base = BER_BVNULL; 47 ber_len_t siz, off, i; 48 49 Debug( LDAP_DEBUG_TRACE, "%s do_search\n", 50 op->o_log_prefix ); 51 /* 52 * Parse the search request. It looks like this: 53 * 54 * SearchRequest := [APPLICATION 3] SEQUENCE { 55 * baseObject DistinguishedName, 56 * scope ENUMERATED { 57 * baseObject (0), 58 * singleLevel (1), 59 * wholeSubtree (2), 60 * subordinate (3) -- OpenLDAP extension 61 * }, 62 * derefAliases ENUMERATED { 63 * neverDerefaliases (0), 64 * derefInSearching (1), 65 * derefFindingBaseObj (2), 66 * alwaysDerefAliases (3) 67 * }, 68 * sizelimit INTEGER (0 .. 65535), 69 * timelimit INTEGER (0 .. 65535), 70 * attrsOnly BOOLEAN, 71 * filter Filter, 72 * attributes SEQUENCE OF AttributeType 73 * } 74 */ 75 76 /* baseObject, scope, derefAliases, sizelimit, timelimit, attrsOnly */ 77 if ( ber_scanf( op->o_ber, "{miiiib" /*}*/, 78 &base, &op->ors_scope, &op->ors_deref, &op->ors_slimit, 79 &op->ors_tlimit, &op->ors_attrsonly ) == LBER_ERROR ) 80 { 81 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" ); 82 rs->sr_err = SLAPD_DISCONNECT; 83 goto return_results; 84 } 85 86 if ( op->ors_tlimit < 0 || op->ors_tlimit > SLAP_MAX_LIMIT ) { 87 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid time limit" ); 88 goto return_results; 89 } 90 91 if ( op->ors_slimit < 0 || op->ors_slimit > SLAP_MAX_LIMIT ) { 92 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid size limit" ); 93 goto return_results; 94 } 95 96 switch( op->ors_scope ) { 97 case LDAP_SCOPE_BASE: 98 case LDAP_SCOPE_ONELEVEL: 99 case LDAP_SCOPE_SUBTREE: 100 case LDAP_SCOPE_SUBORDINATE: 101 break; 102 default: 103 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid scope" ); 104 goto return_results; 105 } 106 107 switch( op->ors_deref ) { 108 case LDAP_DEREF_NEVER: 109 case LDAP_DEREF_FINDING: 110 case LDAP_DEREF_SEARCHING: 111 case LDAP_DEREF_ALWAYS: 112 break; 113 default: 114 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid deref" ); 115 goto return_results; 116 } 117 118 rs->sr_err = dnPrettyNormal( NULL, &base, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx ); 119 if( rs->sr_err != LDAP_SUCCESS ) { 120 Debug( LDAP_DEBUG_ANY, "%s do_search: invalid dn: \"%s\"\n", 121 op->o_log_prefix, base.bv_val ); 122 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" ); 123 goto return_results; 124 } 125 126 Debug( LDAP_DEBUG_ARGS, "SRCH \"%s\" %d %d %d %d %d\n", 127 base.bv_val, op->ors_scope, op->ors_deref, 128 op->ors_slimit, op->ors_tlimit, op->ors_attrsonly); 129 130 /* filter - returns a "normalized" version */ 131 rs->sr_err = get_filter( op, op->o_ber, &op->ors_filter, &rs->sr_text ); 132 if( rs->sr_err != LDAP_SUCCESS ) { 133 if( rs->sr_err == SLAPD_DISCONNECT ) { 134 rs->sr_err = LDAP_PROTOCOL_ERROR; 135 send_ldap_disconnect( op, rs ); 136 rs->sr_err = SLAPD_DISCONNECT; 137 } else { 138 send_ldap_result( op, rs ); 139 } 140 goto return_results; 141 } 142 filter2bv_x( op, op->ors_filter, &op->ors_filterstr ); 143 144 Debug( LDAP_DEBUG_ARGS, " filter: %s\n", 145 !BER_BVISEMPTY( &op->ors_filterstr ) ? op->ors_filterstr.bv_val : "empty" ); 146 147 /* attributes */ 148 siz = sizeof(AttributeName); 149 off = offsetof(AttributeName,an_name); 150 if ( ber_scanf( op->o_ber, "{M}}", &op->ors_attrs, &siz, off ) == LBER_ERROR ) { 151 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding attrs error" ); 152 rs->sr_err = SLAPD_DISCONNECT; 153 goto return_results; 154 } 155 for ( i=0; i<siz; i++ ) { 156 const char *dummy; /* ignore msgs from bv2ad */ 157 op->ors_attrs[i].an_desc = NULL; 158 op->ors_attrs[i].an_oc = NULL; 159 op->ors_attrs[i].an_flags = 0; 160 if ( slap_bv2ad( &op->ors_attrs[i].an_name, 161 &op->ors_attrs[i].an_desc, &dummy ) != LDAP_SUCCESS ) 162 { 163 if ( slap_bv2undef_ad( &op->ors_attrs[i].an_name, 164 &op->ors_attrs[i].an_desc, &dummy, 165 SLAP_AD_PROXIED|SLAP_AD_NOINSERT ) ) 166 { 167 struct berval *bv = &op->ors_attrs[i].an_name; 168 169 /* RFC 4511 LDAPv3: All User Attributes */ 170 if ( bvmatch( bv, slap_bv_all_user_attrs ) ) { 171 continue; 172 } 173 174 /* RFC 3673 LDAPv3: All Operational Attributes */ 175 if ( bvmatch( bv, slap_bv_all_operational_attrs ) ) { 176 continue; 177 } 178 179 /* RFC 4529 LDAP: Requesting Attributes by Object Class */ 180 if ( bv->bv_len > 1 && bv->bv_val[0] == '@' ) { 181 /* FIXME: check if remaining is valid oc name? */ 182 continue; 183 } 184 185 /* add more "exceptions" to RFC 4511 4.5.1.8. */ 186 187 /* invalid attribute description? remove */ 188 if ( ad_keystring( bv ) ) { 189 /* NOTE: parsed in-place, don't modify; 190 * rather add "1.1", which must be ignored */ 191 BER_BVSTR( &op->ors_attrs[i].an_name, LDAP_NO_ATTRS ); 192 } 193 194 /* otherwise leave in place... */ 195 } 196 } 197 } 198 199 if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) { 200 Debug( LDAP_DEBUG_ANY, "%s do_search: get_ctrls failed\n", 201 op->o_log_prefix ); 202 goto return_results; 203 } 204 205 if (LogTest( LDAP_DEBUG_ARGS ) ) { 206 char abuf[BUFSIZ/2], *ptr = abuf; 207 unsigned len = 0, alen; 208 209 if ( !siz ) { 210 len = 1; 211 abuf[0] = '\0'; 212 } 213 for ( i = 0; i<siz; i++ ) { 214 alen = op->ors_attrs[i].an_name.bv_len; 215 if (alen >= sizeof(abuf)) { 216 alen = sizeof(abuf)-1; 217 } 218 if (len && (len + 1 + alen >= sizeof(abuf))) { 219 Debug( LDAP_DEBUG_ARGS, " attrs: %s\n", abuf ); 220 len = 0; 221 ptr = abuf; 222 } 223 if (len) { 224 *ptr++ = ' '; 225 len++; 226 } 227 ptr = lutil_strncopy(ptr, op->ors_attrs[i].an_name.bv_val, alen); 228 len += alen; 229 *ptr = '\0'; 230 } 231 if (len) { 232 Debug( LDAP_DEBUG_ARGS, " attrs: %s\n", abuf ); 233 } 234 } 235 236 if (LogTest( LDAP_DEBUG_STATS ) ) { 237 char abuf[BUFSIZ/2], *ptr = abuf; 238 unsigned len = 0, alen; 239 240 sprintf(abuf, "scope=%d deref=%d", op->ors_scope, op->ors_deref); 241 Debug( LDAP_DEBUG_STATS, 242 "%s SRCH base=\"%s\" %s filter=\"%s\"\n", 243 op->o_log_prefix, op->o_req_dn.bv_val, abuf, 244 op->ors_filterstr.bv_val ); 245 246 for ( i = 0; i<siz; i++ ) { 247 alen = op->ors_attrs[i].an_name.bv_len; 248 if (alen >= sizeof(abuf)) { 249 alen = sizeof(abuf)-1; 250 } 251 if (len && (len + 1 + alen >= sizeof(abuf))) { 252 Debug( LDAP_DEBUG_STATS, "%s SRCH attr=%s\n", 253 op->o_log_prefix, abuf ); 254 len = 0; 255 ptr = abuf; 256 } 257 if (len) { 258 *ptr++ = ' '; 259 len++; 260 } 261 ptr = lutil_strncopy(ptr, op->ors_attrs[i].an_name.bv_val, alen); 262 len += alen; 263 *ptr = '\0'; 264 } 265 if (len) { 266 Debug( LDAP_DEBUG_STATS, "%s SRCH attr=%s\n", 267 op->o_log_prefix, abuf ); 268 } 269 } 270 271 op->o_bd = frontendDB; 272 rs->sr_err = frontendDB->be_search( op, rs ); 273 if ( rs->sr_err == SLAPD_ASYNCOP ) { 274 /* skip cleanup */ 275 return rs->sr_err; 276 } 277 278 return_results:; 279 if ( !BER_BVISNULL( &op->o_req_dn ) ) { 280 slap_sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx ); 281 } 282 if ( !BER_BVISNULL( &op->o_req_ndn ) ) { 283 slap_sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx ); 284 } 285 if ( !BER_BVISNULL( &op->ors_filterstr ) ) { 286 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 287 } 288 if ( op->ors_filter != NULL) { 289 filter_free_x( op, op->ors_filter, 1 ); 290 } 291 if ( op->ors_attrs != NULL ) { 292 op->o_tmpfree( op->ors_attrs, op->o_tmpmemctx ); 293 } 294 295 return rs->sr_err; 296 } 297 298 int 299 fe_op_search( Operation *op, SlapReply *rs ) 300 { 301 BackendDB *bd = op->o_bd; 302 303 if ( op->ors_scope == LDAP_SCOPE_BASE ) { 304 Entry *entry = NULL; 305 306 if ( BER_BVISEMPTY( &op->o_req_ndn ) ) { 307 #ifdef LDAP_CONNECTIONLESS 308 /* Ignore LDAPv2 CLDAP Root DSE queries */ 309 if (op->o_protocol == LDAP_VERSION2 && op->o_conn->c_is_udp) { 310 goto return_results; 311 } 312 #endif 313 /* check restrictions */ 314 if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) { 315 send_ldap_result( op, rs ); 316 goto return_results; 317 } 318 319 rs->sr_err = root_dse_info( op->o_conn, &entry, &rs->sr_text ); 320 321 } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) { 322 /* check restrictions */ 323 if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) { 324 send_ldap_result( op, rs ); 325 goto return_results; 326 } 327 328 rs->sr_err = schema_info( &entry, &rs->sr_text ); 329 } 330 331 if( rs->sr_err != LDAP_SUCCESS ) { 332 send_ldap_result( op, rs ); 333 goto return_results; 334 335 } else if ( entry != NULL ) { 336 if ( get_assert( op ) && 337 ( test_filter( op, entry, get_assertion( op )) != LDAP_COMPARE_TRUE )) { 338 rs->sr_err = LDAP_ASSERTION_FAILED; 339 goto fail1; 340 } 341 342 rs->sr_err = test_filter( op, entry, op->ors_filter ); 343 344 if( rs->sr_err == LDAP_COMPARE_TRUE ) { 345 /* note: we set no limits because either 346 * no limit is specified, or at least 1 347 * is specified, and we're going to return 348 * at most one entry */ 349 op->ors_slimit = SLAP_NO_LIMIT; 350 op->ors_tlimit = SLAP_NO_LIMIT; 351 352 rs->sr_entry = entry; 353 rs->sr_attrs = op->ors_attrs; 354 rs->sr_operational_attrs = NULL; 355 rs->sr_flags = 0; 356 send_search_entry( op, rs ); 357 rs->sr_entry = NULL; 358 rs->sr_operational_attrs = NULL; 359 } 360 rs->sr_err = LDAP_SUCCESS; 361 fail1: 362 entry_free( entry ); 363 send_ldap_result( op, rs ); 364 goto return_results; 365 } 366 } 367 368 if( BER_BVISEMPTY( &op->o_req_ndn ) && !BER_BVISEMPTY( &default_search_nbase ) ) { 369 slap_sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx ); 370 slap_sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx ); 371 372 ber_dupbv_x( &op->o_req_dn, &default_search_base, op->o_tmpmemctx ); 373 ber_dupbv_x( &op->o_req_ndn, &default_search_nbase, op->o_tmpmemctx ); 374 } 375 376 /* 377 * We could be serving multiple database backends. Select the 378 * appropriate one, or send a referral to our "referral server" 379 * if we don't hold it. 380 */ 381 382 op->o_bd = select_backend( &op->o_req_ndn, 1 ); 383 if ( op->o_bd == NULL ) { 384 rs->sr_ref = referral_rewrite( default_referral, 385 NULL, &op->o_req_dn, op->ors_scope ); 386 387 if (!rs->sr_ref) rs->sr_ref = default_referral; 388 rs->sr_err = LDAP_REFERRAL; 389 op->o_bd = bd; 390 send_ldap_result( op, rs ); 391 392 if (rs->sr_ref != default_referral) 393 ber_bvarray_free( rs->sr_ref ); 394 rs->sr_ref = NULL; 395 goto return_results; 396 } 397 398 /* check restrictions */ 399 if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) { 400 send_ldap_result( op, rs ); 401 goto return_results; 402 } 403 404 /* check for referrals */ 405 if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) { 406 goto return_results; 407 } 408 409 if ( SLAP_SHADOW(op->o_bd) && get_dontUseCopy(op) ) { 410 /* don't use shadow copy */ 411 BerVarray defref = op->o_bd->be_update_refs 412 ? op->o_bd->be_update_refs : default_referral; 413 414 if( defref != NULL ) { 415 rs->sr_ref = referral_rewrite( defref, 416 NULL, &op->o_req_dn, op->ors_scope ); 417 if( !rs->sr_ref) rs->sr_ref = defref; 418 rs->sr_err = LDAP_REFERRAL; 419 send_ldap_result( op, rs ); 420 421 if (rs->sr_ref != defref) ber_bvarray_free( rs->sr_ref ); 422 423 } else { 424 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 425 "copy not used; no referral information available" ); 426 } 427 428 } else if ( op->o_bd->be_search ) { 429 if ( limits_check( op, rs ) == 0 ) { 430 /* actually do the search and send the result(s) */ 431 (op->o_bd->be_search)( op, rs ); 432 } 433 /* else limits_check() sends error */ 434 435 } else { 436 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 437 "operation not supported within namingContext" ); 438 } 439 440 return_results:; 441 op->o_bd = bd; 442 return rs->sr_err; 443 } 444 445