Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: search.c,v 1.4 2025/09/05 21:16:21 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) 1990 Regents of the University of Michigan.
     18  * All rights reserved.
     19  */
     20 
     21 #include <sys/cdefs.h>
     22 __RCSID("$NetBSD: search.c,v 1.4 2025/09/05 21:16:21 christos Exp $");
     23 
     24 #include "portable.h"
     25 
     26 #include <stdio.h>
     27 
     28 #include <ac/stdlib.h>
     29 
     30 #include <ac/socket.h>
     31 #include <ac/string.h>
     32 #include <ac/time.h>
     33 
     34 #include "ldap-int.h"
     35 #include "ldap_log.h"
     36 
     37 /*
     38  * ldap_search_ext - initiate an ldap search operation.
     39  *
     40  * Parameters:
     41  *
     42  *	ld		LDAP descriptor
     43  *	base		DN of the base object
     44  *	scope		the search scope - one of
     45  *				LDAP_SCOPE_BASE (baseObject),
     46  *			    LDAP_SCOPE_ONELEVEL (oneLevel),
     47  *				LDAP_SCOPE_SUBTREE (subtree), or
     48  *				LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension
     49  *	filter		a string containing the search filter
     50  *			(e.g., "(|(cn=bob)(sn=bob))")
     51  *	attrs		list of attribute types to return for matches
     52  *	attrsonly	1 => attributes only 0 => attributes and values
     53  *
     54  * Example:
     55  *	char	*attrs[] = { "mail", "title", 0 };
     56  *	ldap_search_ext( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob",
     57  *	    attrs, attrsonly, sctrls, ctrls, timeout, sizelimit,
     58  *		&msgid );
     59  */
     60 int
     61 ldap_search_ext(
     62 	LDAP *ld,
     63 	LDAP_CONST char *base,
     64 	int scope,
     65 	LDAP_CONST char *filter,
     66 	char **attrs,
     67 	int attrsonly,
     68 	LDAPControl **sctrls,
     69 	LDAPControl **cctrls,
     70 	struct timeval *timeout,
     71 	int sizelimit,
     72 	int *msgidp )
     73 {
     74 	return ldap_pvt_search( ld, base, scope, filter, attrs,
     75 		attrsonly, sctrls, cctrls, timeout, sizelimit, -1, msgidp );
     76 }
     77 
     78 int
     79 ldap_pvt_search(
     80 	LDAP *ld,
     81 	LDAP_CONST char *base,
     82 	int scope,
     83 	LDAP_CONST char *filter,
     84 	char **attrs,
     85 	int attrsonly,
     86 	LDAPControl **sctrls,
     87 	LDAPControl **cctrls,
     88 	struct timeval *timeout,
     89 	int sizelimit,
     90 	int deref,
     91 	int *msgidp )
     92 {
     93 	int rc;
     94 	BerElement	*ber;
     95 	int timelimit;
     96 	ber_int_t id;
     97 
     98 	Debug0( LDAP_DEBUG_TRACE, "ldap_search_ext\n" );
     99 
    100 	assert( ld != NULL );
    101 	assert( LDAP_VALID( ld ) );
    102 
    103 	/* check client controls */
    104 	rc = ldap_int_client_controls( ld, cctrls );
    105 	if( rc != LDAP_SUCCESS ) return rc;
    106 
    107 	/*
    108 	 * if timeout is provided, both tv_sec and tv_usec must
    109 	 * not be zero
    110 	 */
    111 	if( timeout != NULL ) {
    112 		if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) {
    113 			return LDAP_PARAM_ERROR;
    114 		}
    115 
    116 		/* timelimit must be non-zero if timeout is provided */
    117 		timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1;
    118 
    119 	} else {
    120 		/* no timeout, no timelimit */
    121 		timelimit = -1;
    122 	}
    123 
    124 	ber = ldap_build_search_req( ld, base, scope, filter, attrs,
    125 	    attrsonly, sctrls, cctrls, timelimit, sizelimit, deref, &id );
    126 
    127 	if ( ber == NULL ) {
    128 		return ld->ld_errno;
    129 	}
    130 
    131 
    132 	/* send the message */
    133 	*msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id );
    134 
    135 	if( *msgidp < 0 )
    136 		return ld->ld_errno;
    137 
    138 	return LDAP_SUCCESS;
    139 }
    140 
    141 int
    142 ldap_search_ext_s(
    143 	LDAP *ld,
    144 	LDAP_CONST char *base,
    145 	int scope,
    146 	LDAP_CONST char *filter,
    147 	char **attrs,
    148 	int attrsonly,
    149 	LDAPControl **sctrls,
    150 	LDAPControl **cctrls,
    151 	struct timeval *timeout,
    152 	int sizelimit,
    153 	LDAPMessage **res )
    154 {
    155 	return ldap_pvt_search_s( ld, base, scope, filter, attrs,
    156 		attrsonly, sctrls, cctrls, timeout, sizelimit, -1, res );
    157 }
    158 
    159 int
    160 ldap_pvt_search_s(
    161 	LDAP *ld,
    162 	LDAP_CONST char *base,
    163 	int scope,
    164 	LDAP_CONST char *filter,
    165 	char **attrs,
    166 	int attrsonly,
    167 	LDAPControl **sctrls,
    168 	LDAPControl **cctrls,
    169 	struct timeval *timeout,
    170 	int sizelimit,
    171 	int deref,
    172 	LDAPMessage **res )
    173 {
    174 	int rc;
    175 	int	msgid;
    176 
    177     *res = NULL;
    178 
    179 	rc = ldap_pvt_search( ld, base, scope, filter, attrs, attrsonly,
    180 		sctrls, cctrls, timeout, sizelimit, deref, &msgid );
    181 
    182 	if ( rc != LDAP_SUCCESS ) {
    183 		return( rc );
    184 	}
    185 
    186 	rc = ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res );
    187 
    188 	if( rc <= 0 ) {
    189 		/* error(-1) or timeout(0) */
    190 		if ( ld->ld_errno == LDAP_TIMEOUT ) {
    191 			/* cleanup request */
    192 			(void) ldap_abandon( ld, msgid );
    193 			ld->ld_errno = LDAP_TIMEOUT;
    194 		}
    195 		return( ld->ld_errno );
    196 	}
    197 
    198 	if( rc == LDAP_RES_SEARCH_REFERENCE || rc == LDAP_RES_INTERMEDIATE ) {
    199 		return( ld->ld_errno );
    200 	}
    201 
    202 	return( ldap_result2error( ld, *res, 0 ) );
    203 }
    204 
    205 /*
    206  * ldap_search - initiate an ldap search operation.
    207  *
    208  * Parameters:
    209  *
    210  *	ld		LDAP descriptor
    211  *	base		DN of the base object
    212  *	scope		the search scope - one of
    213  *				LDAP_SCOPE_BASE (baseObject),
    214  *			    LDAP_SCOPE_ONELEVEL (oneLevel),
    215  *				LDAP_SCOPE_SUBTREE (subtree), or
    216  *				LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension
    217  *	filter		a string containing the search filter
    218  *			(e.g., "(|(cn=bob)(sn=bob))")
    219  *	attrs		list of attribute types to return for matches
    220  *	attrsonly	1 => attributes only 0 => attributes and values
    221  *
    222  * Example:
    223  *	char	*attrs[] = { "mail", "title", 0 };
    224  *	msgid = ldap_search( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob",
    225  *	    attrs, attrsonly );
    226  */
    227 int
    228 ldap_search(
    229 	LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
    230 	char **attrs, int attrsonly )
    231 {
    232 	BerElement	*ber;
    233 	ber_int_t	id;
    234 
    235 	Debug0( LDAP_DEBUG_TRACE, "ldap_search\n" );
    236 
    237 	assert( ld != NULL );
    238 	assert( LDAP_VALID( ld ) );
    239 
    240 	ber = ldap_build_search_req( ld, base, scope, filter, attrs,
    241 	    attrsonly, NULL, NULL, -1, -1, -1, &id );
    242 
    243 	if ( ber == NULL ) {
    244 		return( -1 );
    245 	}
    246 
    247 
    248 	/* send the message */
    249 	return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id ));
    250 }
    251 
    252 
    253 BerElement *
    254 ldap_build_search_req(
    255 	LDAP *ld,
    256 	LDAP_CONST char *base,
    257 	ber_int_t scope,
    258 	LDAP_CONST char *filter,
    259 	char **attrs,
    260 	ber_int_t attrsonly,
    261 	LDAPControl **sctrls,
    262 	LDAPControl **cctrls,
    263 	ber_int_t timelimit,
    264 	ber_int_t sizelimit,
    265 	ber_int_t deref,
    266 	ber_int_t *idp)
    267 {
    268 	BerElement	*ber;
    269 	int		err;
    270 
    271 	/*
    272 	 * Create the search request.  It looks like this:
    273 	 *	SearchRequest := [APPLICATION 3] SEQUENCE {
    274 	 *		baseObject	DistinguishedName,
    275 	 *		scope		ENUMERATED {
    276 	 *			baseObject	(0),
    277 	 *			singleLevel	(1),
    278 	 *			wholeSubtree	(2)
    279 	 *		},
    280 	 *		derefAliases	ENUMERATED {
    281 	 *			neverDerefaliases	(0),
    282 	 *			derefInSearching	(1),
    283 	 *			derefFindingBaseObj	(2),
    284 	 *			alwaysDerefAliases	(3)
    285 	 *		},
    286 	 *		sizelimit	INTEGER (0 .. 65535),
    287 	 *		timelimit	INTEGER (0 .. 65535),
    288 	 *		attrsOnly	BOOLEAN,
    289 	 *		filter		Filter,
    290 	 *		attributes	SEQUENCE OF AttributeType
    291 	 *	}
    292 	 * wrapped in an ldap message.
    293 	 */
    294 
    295 	/* create a message to send */
    296 	if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
    297 		return( NULL );
    298 	}
    299 
    300 	if ( base == NULL ) {
    301 		/* no base provided, use session default base */
    302 		base = ld->ld_options.ldo_defbase;
    303 
    304 		if ( base == NULL ) {
    305 			/* no session default base, use top */
    306 			base = "";
    307 		}
    308 	}
    309 
    310 	LDAP_NEXT_MSGID( ld, *idp );
    311 #ifdef LDAP_CONNECTIONLESS
    312 	if ( LDAP_IS_UDP(ld) ) {
    313 		struct sockaddr_storage sa = {0};
    314 		/* dummy, filled with ldo_peer in request.c */
    315 	    err = ber_write( ber, (char *) &sa, sizeof( sa ), 0 );
    316 	}
    317 	if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == LDAP_VERSION2) {
    318 	    char *dn = ld->ld_options.ldo_cldapdn;
    319 	    if (!dn) dn = "";
    320 	    err = ber_printf( ber, "{ist{seeiib", *idp, dn,
    321 		LDAP_REQ_SEARCH, base, (ber_int_t) scope,
    322 		(deref < 0) ? ld->ld_deref : deref,
    323 		(sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
    324 		(timelimit < 0) ? ld->ld_timelimit : timelimit,
    325 		attrsonly );
    326 	} else
    327 #endif
    328 	{
    329 	    err = ber_printf( ber, "{it{seeiib", *idp,
    330 		LDAP_REQ_SEARCH, base, (ber_int_t) scope,
    331 		(deref < 0) ? ld->ld_deref : deref,
    332 		(sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
    333 		(timelimit < 0) ? ld->ld_timelimit : timelimit,
    334 		attrsonly );
    335 	}
    336 
    337 	if ( err == -1 ) {
    338 		ld->ld_errno = LDAP_ENCODING_ERROR;
    339 		ber_free( ber, 1 );
    340 		return( NULL );
    341 	}
    342 
    343 	if( filter == NULL ) {
    344 		filter = "(objectclass=*)";
    345 	}
    346 
    347 	err = ldap_pvt_put_filter( ber, filter );
    348 
    349 	if ( err  == -1 ) {
    350 		ld->ld_errno = LDAP_FILTER_ERROR;
    351 		ber_free( ber, 1 );
    352 		return( NULL );
    353 	}
    354 
    355 #ifdef LDAP_DEBUG
    356 	if ( ldap_debug & LDAP_DEBUG_ARGS ) {
    357 		char	buf[ BUFSIZ ], *ptr = " *";
    358 
    359 		if ( attrs != NULL ) {
    360 			int	i, len, rest = sizeof( buf );
    361 
    362 			for ( i = 0; attrs[ i ] != NULL && rest > 0; i++ ) {
    363 				ptr = &buf[ sizeof( buf ) - rest ];
    364 				len = snprintf( ptr, rest, " %s", attrs[ i ] );
    365 				rest -= (len >= 0 ? len : (int) sizeof( buf ));
    366 			}
    367 
    368 			if ( rest <= 0 ) {
    369 				AC_MEMCPY( &buf[ sizeof( buf ) - STRLENOF( "...(truncated)" ) - 1 ],
    370 					"...(truncated)", STRLENOF( "...(truncated)" ) + 1 );
    371 			}
    372 			ptr = buf;
    373 		}
    374 
    375 		Debug1( LDAP_DEBUG_ARGS, "ldap_build_search_req ATTRS:%s\n", ptr );
    376 	}
    377 #endif /* LDAP_DEBUG */
    378 
    379 	if ( ber_printf( ber, /*{*/ "{v}N}", attrs ) == -1 ) {
    380 		ld->ld_errno = LDAP_ENCODING_ERROR;
    381 		ber_free( ber, 1 );
    382 		return( NULL );
    383 	}
    384 
    385 	/* Put Server Controls */
    386 	if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
    387 		ber_free( ber, 1 );
    388 		return( NULL );
    389 	}
    390 
    391 	if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
    392 		ld->ld_errno = LDAP_ENCODING_ERROR;
    393 		ber_free( ber, 1 );
    394 		return( NULL );
    395 	}
    396 
    397 	return( ber );
    398 }
    399 
    400 int
    401 ldap_search_st(
    402 	LDAP *ld, LDAP_CONST char *base, int scope,
    403 	LDAP_CONST char *filter, char **attrs,
    404 	int attrsonly, struct timeval *timeout, LDAPMessage **res )
    405 {
    406 	int	msgid;
    407 
    408     *res = NULL;
    409 
    410 	if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
    411 	    == -1 )
    412 		return( ld->ld_errno );
    413 
    414 	if ( ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res ) == -1 || !*res )
    415 		return( ld->ld_errno );
    416 
    417 	if ( ld->ld_errno == LDAP_TIMEOUT ) {
    418 		(void) ldap_abandon( ld, msgid );
    419 		ld->ld_errno = LDAP_TIMEOUT;
    420 		return( ld->ld_errno );
    421 	}
    422 
    423 	return( ldap_result2error( ld, *res, 0 ) );
    424 }
    425 
    426 int
    427 ldap_search_s(
    428 	LDAP *ld,
    429 	LDAP_CONST char *base,
    430 	int scope,
    431 	LDAP_CONST char *filter,
    432 	char **attrs,
    433 	int attrsonly,
    434 	LDAPMessage **res )
    435 {
    436 	int	msgid;
    437 
    438     *res = NULL;
    439 
    440 	if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
    441 	    == -1 )
    442 		return( ld->ld_errno );
    443 
    444 	if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, res ) == -1 || !*res )
    445 		return( ld->ld_errno );
    446 
    447 	return( ldap_result2error( ld, *res, 0 ) );
    448 }
    449 
    450 static char escape[128] = {
    451 	1, 1, 1, 1, 1, 1, 1, 1,
    452 	1, 1, 1, 1, 1, 1, 1, 1,
    453 	1, 1, 1, 1, 1, 1, 1, 1,
    454 	1, 1, 1, 1, 1, 1, 1, 1,
    455 
    456 	0, 0, 0, 0, 0, 0, 0, 0,
    457 	1, 1, 1, 0, 0, 0, 0, 0,
    458 	0, 0, 0, 0, 0, 0, 0, 0,
    459 	0, 0, 0, 0, 0, 0, 0, 0,
    460 
    461 	0, 0, 0, 0, 0, 0, 0, 0,
    462 	0, 0, 0, 0, 0, 0, 0, 0,
    463 	0, 0, 0, 0, 0, 0, 0, 0,
    464 	0, 0, 0, 0, 1, 0, 0, 0,
    465 
    466 	0, 0, 0, 0, 0, 0, 0, 0,
    467 	0, 0, 0, 0, 0, 0, 0, 0,
    468 	0, 0, 0, 0, 0, 0, 0, 0,
    469 	0, 0, 0, 0, 0, 0, 0, 1
    470 };
    471 #define	NEEDFLTESCAPE(c)	((c) & 0x80 || escape[ (unsigned)(c) ])
    472 
    473 /*
    474  * compute the length of the escaped value
    475  */
    476 ber_len_t
    477 ldap_bv2escaped_filter_value_len( struct berval *in )
    478 {
    479 	ber_len_t	i, l;
    480 
    481 	assert( in != NULL );
    482 
    483 	if ( in->bv_len == 0 ) {
    484 		return 0;
    485 	}
    486 
    487 	for( l = 0, i = 0; i < in->bv_len; l++, i++ ) {
    488 		char c = in->bv_val[ i ];
    489 		if ( NEEDFLTESCAPE( c ) ) {
    490 			l += 2;
    491 		}
    492 	}
    493 
    494 	return l;
    495 }
    496 
    497 int
    498 ldap_bv2escaped_filter_value( struct berval *in, struct berval *out )
    499 {
    500 	return ldap_bv2escaped_filter_value_x( in, out, 0, NULL );
    501 }
    502 
    503 int
    504 ldap_bv2escaped_filter_value_x( struct berval *in, struct berval *out, int inplace, void *ctx )
    505 {
    506 	ber_len_t	i, l;
    507 
    508 	assert( in != NULL );
    509 	assert( out != NULL );
    510 
    511 	BER_BVZERO( out );
    512 
    513 	if ( in->bv_len == 0 ) {
    514 		return 0;
    515 	}
    516 
    517 	/* assume we'll escape everything */
    518 	l = ldap_bv2escaped_filter_value_len( in );
    519 	if ( l == in->bv_len ) {
    520 		if ( inplace ) {
    521 			*out = *in;
    522 		} else {
    523 			ber_dupbv( out, in );
    524 		}
    525 		return 0;
    526 	}
    527 	out->bv_val = LDAP_MALLOCX( l + 1, ctx );
    528 	if ( out->bv_val == NULL ) {
    529 		return -1;
    530 	}
    531 
    532 	for ( i = 0; i < in->bv_len; i++ ) {
    533 		char c = in->bv_val[ i ];
    534 		if ( NEEDFLTESCAPE( c ) ) {
    535 			assert( out->bv_len < l - 2 );
    536 			out->bv_val[out->bv_len++] = '\\';
    537 			out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & (c>>4)];
    538 			out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & c];
    539 
    540 		} else {
    541 			assert( out->bv_len < l );
    542 			out->bv_val[out->bv_len++] = c;
    543 		}
    544 	}
    545 
    546 	out->bv_val[out->bv_len] = '\0';
    547 
    548 	return 0;
    549 }
    550 
    551