1 /* $NetBSD: search.c,v 1.2 2020/08/11 13:15:37 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2020 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.2 2020/08/11 13:15:37 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 Debug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 ); 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 Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 ); 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 Debug( LDAP_DEBUG_ARGS, "ldap_build_search_req ATTRS:%s\n", ptr, 0,0 ); 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