mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-21 23:29:34 -05:00
This could cause problems on odd systems. The generic headers should be extended as needed to include necessary system headers or, if necessary, make explicit declarations. Extended ac/string.h header to look for string.h/strings.h if STDC_HEADERS is not defined. Also provide basic declarations for str*() functions. This could cause problems on odd systems. Extended ac/unistd.h header to define basic declaration for misc functions that might be missing from headers. This includes externs for getenv(), getopt(), mktemp(), tempname(). Protect fax500.h from multiple inclusion. Moved includes of system/generic headers back to source files. Made mail500 helper functions static. Fixed includes of ctype.h, signal.h, etc. to use generics. lutil/tempname.c: was including stdlib.h twice, one should stdio.h. Wrapped <sys/resource.h> with HAVE_SYS_RESOURCE_H. lber/io.c/ber_get_next(): Changed noctets back to signed. Used with BerRead which expects signed int as second arg and returns signed int.
552 lines
12 KiB
C
552 lines
12 KiB
C
/*
|
|
* Copyright (c) 1990 Regents of the University of Michigan.
|
|
* All rights reserved.
|
|
*
|
|
* search.c
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#ifndef lint
|
|
static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <ac/ctype.h>
|
|
#include <ac/socket.h>
|
|
#include <ac/string.h>
|
|
#include <ac/time.h>
|
|
|
|
#include "ldap-int.h"
|
|
|
|
static char *find_right_paren LDAP_P(( char *s ));
|
|
static char *put_complex_filter LDAP_P(( BerElement *ber, char *str,
|
|
unsigned long tag, int not ));
|
|
static int put_filter LDAP_P(( BerElement *ber, char *str ));
|
|
static int put_simple_filter LDAP_P(( BerElement *ber, char *str ));
|
|
static int put_substring_filter LDAP_P(( BerElement *ber, char *type, char *str ));
|
|
static int put_filter_list LDAP_P(( BerElement *ber, char *str ));
|
|
|
|
/*
|
|
* ldap_search - initiate an ldap (and X.500) search operation. Parameters:
|
|
*
|
|
* ld LDAP descriptor
|
|
* base DN of the base object
|
|
* scope the search scope - one of LDAP_SCOPE_BASE,
|
|
* LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
|
|
* filter a string containing the search filter
|
|
* (e.g., "(|(cn=bob)(sn=bob))")
|
|
* attrs list of attribute types to return for matches
|
|
* attrsonly 1 => attributes only 0 => attributes and values
|
|
*
|
|
* Example:
|
|
* char *attrs[] = { "mail", "title", 0 };
|
|
* msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
|
|
* attrs, attrsonly );
|
|
*/
|
|
int
|
|
ldap_search( LDAP *ld, char *base, int scope, char *filter,
|
|
char **attrs, int attrsonly )
|
|
{
|
|
BerElement *ber;
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
|
|
|
|
if (( ber = ldap_build_search_req( ld, base, scope, filter, attrs,
|
|
attrsonly )) == NULLBER ) {
|
|
return( -1 );
|
|
}
|
|
|
|
#ifndef LDAP_NOCACHE
|
|
if ( ld->ld_cache != NULL ) {
|
|
if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
|
|
ber_free( ber, 1 );
|
|
ld->ld_errno = LDAP_SUCCESS;
|
|
return( ld->ld_msgid );
|
|
}
|
|
ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
|
|
}
|
|
#endif /* LDAP_NOCACHE */
|
|
|
|
/* send the message */
|
|
return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber ));
|
|
}
|
|
|
|
|
|
BerElement *
|
|
ldap_build_search_req( LDAP *ld, char *base, int scope, char *filter,
|
|
char **attrs, int attrsonly )
|
|
{
|
|
BerElement *ber;
|
|
int err;
|
|
|
|
/*
|
|
* Create the search request. It looks like this:
|
|
* SearchRequest := [APPLICATION 3] SEQUENCE {
|
|
* baseObject DistinguishedName,
|
|
* scope ENUMERATED {
|
|
* baseObject (0),
|
|
* singleLevel (1),
|
|
* wholeSubtree (2)
|
|
* },
|
|
* derefAliases ENUMERATED {
|
|
* neverDerefaliases (0),
|
|
* derefInSearching (1),
|
|
* derefFindingBaseObj (2),
|
|
* alwaysDerefAliases (3)
|
|
* },
|
|
* sizelimit INTEGER (0 .. 65535),
|
|
* timelimit INTEGER (0 .. 65535),
|
|
* attrsOnly BOOLEAN,
|
|
* filter Filter,
|
|
* attributes SEQUENCE OF AttributeType
|
|
* }
|
|
* wrapped in an ldap message.
|
|
*/
|
|
|
|
/* create a message to send */
|
|
if ( (ber = ldap_alloc_ber_with_options( ld )) == NULLBER ) {
|
|
return( NULLBER );
|
|
}
|
|
|
|
if ( base == NULL ) {
|
|
base = "";
|
|
}
|
|
|
|
#ifdef LDAP_CONNECTIONLESS
|
|
if ( ld->ld_sb.sb_naddr > 0 ) {
|
|
err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
|
|
ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
|
|
ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
|
|
} else {
|
|
#endif /* LDAP_CONNECTIONLESS */
|
|
err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
|
|
LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
|
|
ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
|
|
#ifdef LDAP_CONNECTIONLESS
|
|
}
|
|
#endif /* LDAP_CONNECTIONLESS */
|
|
|
|
if ( err == -1 ) {
|
|
ld->ld_errno = LDAP_ENCODING_ERROR;
|
|
ber_free( ber, 1 );
|
|
return( NULLBER );
|
|
}
|
|
|
|
filter = strdup( filter );
|
|
err = put_filter( ber, filter );
|
|
free( filter );
|
|
|
|
if ( err == -1 ) {
|
|
ld->ld_errno = LDAP_FILTER_ERROR;
|
|
ber_free( ber, 1 );
|
|
return( NULLBER );
|
|
}
|
|
|
|
if ( ber_printf( ber, "{v}}}", attrs ) == -1 ) {
|
|
ld->ld_errno = LDAP_ENCODING_ERROR;
|
|
ber_free( ber, 1 );
|
|
return( NULLBER );
|
|
}
|
|
|
|
return( ber );
|
|
}
|
|
|
|
static char *
|
|
find_right_paren( char *s )
|
|
{
|
|
int balance, escape;
|
|
|
|
balance = 1;
|
|
escape = 0;
|
|
while ( *s && balance ) {
|
|
if ( escape == 0 ) {
|
|
if ( *s == '(' )
|
|
balance++;
|
|
else if ( *s == ')' )
|
|
balance--;
|
|
}
|
|
if ( *s == '\\' && ! escape )
|
|
escape = 1;
|
|
else
|
|
escape = 0;
|
|
if ( balance )
|
|
s++;
|
|
}
|
|
|
|
return( *s ? s : NULL );
|
|
}
|
|
|
|
static char *
|
|
put_complex_filter( BerElement *ber, char *str, unsigned long tag, int not )
|
|
{
|
|
char *next;
|
|
|
|
/*
|
|
* We have (x(filter)...) with str sitting on
|
|
* the x. We have to find the paren matching
|
|
* the one before the x and put the intervening
|
|
* filters by calling put_filter_list().
|
|
*/
|
|
|
|
/* put explicit tag */
|
|
if ( ber_printf( ber, "t{", tag ) == -1 )
|
|
return( NULL );
|
|
/*
|
|
if ( !not && ber_printf( ber, "{" ) == -1 )
|
|
return( NULL );
|
|
*/
|
|
|
|
str++;
|
|
if ( (next = find_right_paren( str )) == NULL )
|
|
return( NULL );
|
|
|
|
*next = '\0';
|
|
if ( put_filter_list( ber, str ) == -1 )
|
|
return( NULL );
|
|
*next++ = ')';
|
|
|
|
/* flush explicit tagged thang */
|
|
if ( ber_printf( ber, "}" ) == -1 )
|
|
return( NULL );
|
|
/*
|
|
if ( !not && ber_printf( ber, "}" ) == -1 )
|
|
return( NULL );
|
|
*/
|
|
|
|
return( next );
|
|
}
|
|
|
|
static int
|
|
put_filter( BerElement *ber, char *str )
|
|
{
|
|
char *next, *tmp, *s, *d;
|
|
int parens, balance, escape, gotescape;
|
|
|
|
/*
|
|
* A Filter looks like this:
|
|
* Filter ::= CHOICE {
|
|
* and [0] SET OF Filter,
|
|
* or [1] SET OF Filter,
|
|
* not [2] Filter,
|
|
* equalityMatch [3] AttributeValueAssertion,
|
|
* substrings [4] SubstringFilter,
|
|
* greaterOrEqual [5] AttributeValueAssertion,
|
|
* lessOrEqual [6] AttributeValueAssertion,
|
|
* present [7] AttributeType,,
|
|
* approxMatch [8] AttributeValueAssertion
|
|
* }
|
|
*
|
|
* SubstringFilter ::= SEQUENCE {
|
|
* type AttributeType,
|
|
* SEQUENCE OF CHOICE {
|
|
* initial [0] IA5String,
|
|
* any [1] IA5String,
|
|
* final [2] IA5String
|
|
* }
|
|
* }
|
|
* Note: tags in a choice are always explicit
|
|
*/
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
|
|
|
|
gotescape = parens = 0;
|
|
while ( *str ) {
|
|
switch ( *str ) {
|
|
case '(':
|
|
str++;
|
|
parens++;
|
|
switch ( *str ) {
|
|
case '&':
|
|
Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
|
|
0, 0, 0 );
|
|
|
|
if ( (str = put_complex_filter( ber, str,
|
|
LDAP_FILTER_AND, 0 )) == NULL )
|
|
return( -1 );
|
|
|
|
parens--;
|
|
break;
|
|
|
|
case '|':
|
|
Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
|
|
0, 0, 0 );
|
|
|
|
if ( (str = put_complex_filter( ber, str,
|
|
LDAP_FILTER_OR, 0 )) == NULL )
|
|
return( -1 );
|
|
|
|
parens--;
|
|
break;
|
|
|
|
case '!':
|
|
Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
|
|
0, 0, 0 );
|
|
|
|
if ( (str = put_complex_filter( ber, str,
|
|
LDAP_FILTER_NOT, 1 )) == NULL )
|
|
return( -1 );
|
|
|
|
parens--;
|
|
break;
|
|
|
|
default:
|
|
Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
|
|
0, 0, 0 );
|
|
|
|
balance = 1;
|
|
escape = 0;
|
|
next = str;
|
|
while ( *next && balance ) {
|
|
if ( escape == 0 ) {
|
|
if ( *next == '(' )
|
|
balance++;
|
|
else if ( *next == ')' )
|
|
balance--;
|
|
}
|
|
if ( *next == '\\' && ! escape )
|
|
gotescape = escape = 1;
|
|
else
|
|
escape = 0;
|
|
if ( balance )
|
|
next++;
|
|
}
|
|
if ( balance != 0 )
|
|
return( -1 );
|
|
|
|
*next = '\0';
|
|
tmp = strdup( str );
|
|
if ( gotescape ) {
|
|
escape = 0;
|
|
for ( s = d = tmp; *s; s++ ) {
|
|
if ( *s != '\\' || escape ) {
|
|
*d++ = *s;
|
|
escape = 0;
|
|
} else {
|
|
escape = 1;
|
|
}
|
|
}
|
|
*d = '\0';
|
|
}
|
|
if ( put_simple_filter( ber, tmp ) == -1 ) {
|
|
free( tmp );
|
|
return( -1 );
|
|
}
|
|
free( tmp );
|
|
*next++ = ')';
|
|
str = next;
|
|
parens--;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ')':
|
|
Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
|
|
0 );
|
|
if ( ber_printf( ber, "]" ) == -1 )
|
|
return( -1 );
|
|
str++;
|
|
parens--;
|
|
break;
|
|
|
|
case ' ':
|
|
str++;
|
|
break;
|
|
|
|
default: /* assume it's a simple type=value filter */
|
|
Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
|
|
0 );
|
|
next = strchr( str, '\0' );
|
|
tmp = strdup( str );
|
|
if ( strchr( tmp, '\\' ) != NULL ) {
|
|
escape = 0;
|
|
for ( s = d = tmp; *s; s++ ) {
|
|
if ( *s != '\\' || escape ) {
|
|
*d++ = *s;
|
|
escape = 0;
|
|
} else {
|
|
escape = 1;
|
|
}
|
|
}
|
|
*d = '\0';
|
|
}
|
|
if ( put_simple_filter( ber, tmp ) == -1 ) {
|
|
free( tmp );
|
|
return( -1 );
|
|
}
|
|
free( tmp );
|
|
str = next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return( parens ? -1 : 0 );
|
|
}
|
|
|
|
/*
|
|
* Put a list of filters like this "(filter1)(filter2)..."
|
|
*/
|
|
|
|
static int
|
|
put_filter_list( BerElement *ber, char *str )
|
|
{
|
|
char *next;
|
|
char save;
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
|
|
|
|
while ( *str ) {
|
|
while ( *str && isspace( *str ) )
|
|
str++;
|
|
if ( *str == '\0' )
|
|
break;
|
|
|
|
if ( (next = find_right_paren( str + 1 )) == NULL )
|
|
return( -1 );
|
|
save = *++next;
|
|
|
|
/* now we have "(filter)" with str pointing to it */
|
|
*next = '\0';
|
|
if ( put_filter( ber, str ) == -1 )
|
|
return( -1 );
|
|
*next = save;
|
|
|
|
str = next;
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static int
|
|
put_simple_filter( BerElement *ber, char *str )
|
|
{
|
|
char *s;
|
|
char *value, savechar;
|
|
unsigned long ftype;
|
|
int rc;
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
|
|
|
|
if ( (s = strchr( str, '=' )) == NULL )
|
|
return( -1 );
|
|
value = s + 1;
|
|
*s-- = '\0';
|
|
savechar = *s;
|
|
|
|
switch ( *s ) {
|
|
case '<':
|
|
ftype = LDAP_FILTER_LE;
|
|
*s = '\0';
|
|
break;
|
|
case '>':
|
|
ftype = LDAP_FILTER_GE;
|
|
*s = '\0';
|
|
break;
|
|
case '~':
|
|
ftype = LDAP_FILTER_APPROX;
|
|
*s = '\0';
|
|
break;
|
|
default:
|
|
if ( strchr( value, '*' ) == NULL ) {
|
|
ftype = LDAP_FILTER_EQUALITY;
|
|
} else if ( strcmp( value, "*" ) == 0 ) {
|
|
ftype = LDAP_FILTER_PRESENT;
|
|
} else {
|
|
rc = put_substring_filter( ber, str, value );
|
|
*(value-1) = '=';
|
|
return( rc );
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( ftype == LDAP_FILTER_PRESENT ) {
|
|
rc = ber_printf( ber, "ts", ftype, str );
|
|
} else {
|
|
rc = ber_printf( ber, "t{ss}", ftype, str, value );
|
|
}
|
|
|
|
*s = savechar;
|
|
*(value-1) = '=';
|
|
return( rc == -1 ? rc : 0 );
|
|
}
|
|
|
|
static int
|
|
put_substring_filter( BerElement *ber, char *type, char *val )
|
|
{
|
|
char *nextstar, gotstar = 0;
|
|
unsigned long ftype;
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
|
|
val, 0 );
|
|
|
|
if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 )
|
|
return( -1 );
|
|
|
|
while ( val != NULL ) {
|
|
if ( (nextstar = strchr( val, '*' )) != NULL )
|
|
*nextstar++ = '\0';
|
|
|
|
if ( gotstar == 0 ) {
|
|
ftype = LDAP_SUBSTRING_INITIAL;
|
|
} else if ( nextstar == NULL ) {
|
|
ftype = LDAP_SUBSTRING_FINAL;
|
|
} else {
|
|
ftype = LDAP_SUBSTRING_ANY;
|
|
}
|
|
if ( *val != '\0' ) {
|
|
if ( ber_printf( ber, "ts", ftype, val ) == -1 )
|
|
return( -1 );
|
|
}
|
|
|
|
gotstar = 1;
|
|
if ( nextstar != NULL )
|
|
*(nextstar-1) = '*';
|
|
val = nextstar;
|
|
}
|
|
|
|
if ( ber_printf( ber, "}}" ) == -1 )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
int
|
|
ldap_search_st( LDAP *ld, char *base, int scope, char *filter, char **attrs,
|
|
int attrsonly, struct timeval *timeout, LDAPMessage **res )
|
|
{
|
|
int msgid;
|
|
|
|
if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
|
|
== -1 )
|
|
return( ld->ld_errno );
|
|
|
|
if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
|
|
return( ld->ld_errno );
|
|
|
|
if ( ld->ld_errno == LDAP_TIMEOUT ) {
|
|
(void) ldap_abandon( ld, msgid );
|
|
ld->ld_errno = LDAP_TIMEOUT;
|
|
return( ld->ld_errno );
|
|
}
|
|
|
|
return( ldap_result2error( ld, *res, 0 ) );
|
|
}
|
|
|
|
int
|
|
ldap_search_s( LDAP *ld, char *base, int scope, char *filter, char **attrs,
|
|
int attrsonly, LDAPMessage **res )
|
|
{
|
|
int msgid;
|
|
|
|
if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
|
|
== -1 )
|
|
return( ld->ld_errno );
|
|
|
|
if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
|
|
return( ld->ld_errno );
|
|
|
|
return( ldap_result2error( ld, *res, 0 ) );
|
|
}
|
|
|