/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * $Id: ldap.c,v 1.43 2004/10/06 07:50:18 bagder Exp $ ***************************************************************************/ #include "setup.h" #ifndef CURL_DISABLE_LDAP /* -- WIN32 approved -- */ #include #include #include #include #include #include #include #include #if defined( WIN32 ) # include # include # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_DLFCN_H # include #endif #include "urldata.h" #include #include "sendf.h" #include "escape.h" #include "transfer.h" #include "strequal.h" #include "strtok.h" #include "ldap.h" #include "memory.h" #define _MPRINTF_REPLACE /* use our functions only */ #include #include "memdebug.h" /* WLdap32.dll functions are *not* stdcall. Must call these via __cdecl * pointers in case libcurl was compiled as fastcall (-Gr). */ #if !defined( WIN32 ) && !defined( __cdecl ) #define __cdecl #endif #ifndef LDAP_SIZELIMIT_EXCEEDED #define LDAP_SIZELIMIT_EXCEEDED 4 #endif #define DLOPEN_MODE RTLD_LAZY /*! assume all dlopen() implementations have this */ #if defined( RTLD_LAZY_GLOBAL ) /* It turns out some systems use this: */ # undef DLOPEN_MODE # define DLOPEN_MODE RTLD_LAZY_GLOBAL #elif defined( RTLD_GLOBAL ) # undef DLOPEN_MODE # define DLOPEN_MODE ( RTLD_LAZY | RTLD_GLOBAL ) #endif #define DYNA_GET_FUNCTION( type, fnc ) do { \ ( fnc ) = (type)DynaGetFunction( # fnc ); \ if ( ( fnc ) == NULL ) { \ return CURLE_FUNCTION_NOT_FOUND;} \ } while ( 0 ) /*! CygWin etc. configure could set these, but we don't want it. * Must use WLdap32.dll code. */ #if defined( WIN32 ) #undef HAVE_DLOPEN #undef HAVE_LIBDL #endif typedef void * ( *dynafunc )( void *input ); /*********************************************************************** */ static void *libldap = NULL; #ifndef WIN32 static void *liblber = NULL; #endif static int DynaOpen( const char **mod_name ) { #if defined( HAVE_DLOPEN ) || defined( HAVE_LIBDL ) if ( libldap == NULL ) { /* * libldap.so should be able to resolve its dependency on * liblber.so automatically, but since it does not we will * handle it here by opening liblber.so as global. */ *mod_name = "liblber.so"; liblber = dlopen( *mod_name, DLOPEN_MODE ); /* Assume loading libldap.so will fail if loading of liblber.so failed */ if ( liblber ) { *mod_name = "libldap.so"; libldap = dlopen( *mod_name, RTLD_LAZY ); } } return ( libldap != NULL && liblber != NULL ); #elif defined( WIN32 ) *mod_name = "wldap32.dll"; if ( !libldap ) { libldap = (void*)LoadLibrary( *mod_name ); } return ( libldap != NULL ); #else return ( 0 ); #endif } static void DynaClose( void ) { #if defined( HAVE_DLOPEN ) || defined( HAVE_LIBDL ) if ( libldap ) { dlclose( libldap ); libldap = NULL; } if ( liblber ) { dlclose( liblber ); liblber = NULL; } #elif defined( WIN32 ) if ( libldap ) { FreeLibrary( (HMODULE)libldap ); libldap = NULL; } #endif } static dynafunc DynaGetFunction( const char *name ) { dynafunc func = (dynafunc)NULL; #if defined( HAVE_DLOPEN ) || defined( HAVE_LIBDL ) if ( libldap ) { /* This typecast magic below was brought by Joe Halpin. In ISO C, you * cannot typecast a data pointer to a function pointer, but that's * exactly what we need to do here to avoid compiler warnings on picky * compilers! */ *( void** )( &func ) = dlsym( libldap, name ); } #elif defined( WIN32 ) if ( libldap ) { func = (dynafunc)GetProcAddress( (HINSTANCE)libldap, name ); } #endif return func; } /*********************************************************************** */ typedef struct ldap_url_desc { struct ldap_url_desc *lud_next; char *lud_scheme; char *lud_host; int lud_port; char *lud_dn; char **lud_attrs; int lud_scope; char *lud_filter; char **lud_exts; int lud_crit_exts; } LDAPURLDesc; #ifdef WIN32 static int _ldap_url_parse( const struct connectdata *conn, LDAPURLDesc **ludp ); static void _ldap_free_urldesc( LDAPURLDesc *ludp ); static void ( *ldap_free_urldesc )( LDAPURLDesc * ) = _ldap_free_urldesc; #endif #ifdef DEBUG_LDAP #define LDAP_TRACE( x ) do { \ _ldap_trace( "%u: ", __LINE__ ); \ _ldap_trace x; \ } while ( 0 ) static void _ldap_trace( const char *fmt, ... ); #else #define LDAP_TRACE( x ) ( (void)0 ) #endif CURLcode Curl_ldap( struct connectdata *conn ) { CURLcode status = CURLE_OK; int rc = 0; #ifndef WIN32 int ( *ldap_url_parse )( char *, LDAPURLDesc ** ); void ( *ldap_free_urldesc )( void * ); #endif void *( __cdecl * ldap_init )( char *, int ); int ( __cdecl * ldap_simple_bind_s )( void *, char *, char * ); int ( __cdecl * ldap_unbind_s )( void * ); int ( __cdecl * ldap_search_s )( void *, char *, int, char *, char **, int, void ** ); void *( __cdecl * ldap_first_entry )( void *, void * ); void *( __cdecl * ldap_next_entry )( void *, void * ); char *( __cdecl * ldap_err2string )(int); char *( __cdecl * ldap_get_dn )( void *, void * ); char *( __cdecl * ldap_first_attribute )( void *, void *, void ** ); char *( __cdecl * ldap_next_attribute )( void *, void *, void * ); char **( __cdecl * ldap_get_values )( void *, void *, const char * ); void ( __cdecl * ldap_value_free )( char ** ); void ( __cdecl * ldap_memfree )( void * ); void ( __cdecl * ber_free )( void *, int ); void *server; LDAPURLDesc *ludp = NULL; const char *mod_name; void *result; void *entryIterator; /*! type should be 'LDAPMessage *' */ int num = 0; struct SessionHandle *data = conn->data; infof( data, "LDAP local: %s\n", data->change.url ); if ( !DynaOpen( &mod_name ) ) { failf( data, "The %s LDAP library/libraries couldn't be opened", mod_name ); return CURLE_LIBRARY_NOT_FOUND; } /* The types are needed because ANSI C distinguishes between * pointer-to-object (data) and pointer-to-function. */ DYNA_GET_FUNCTION( void *( * )( char *, int ), ldap_init ); DYNA_GET_FUNCTION( int( * ) ( void *, char *, char * ), ldap_simple_bind_s ); DYNA_GET_FUNCTION( int( * ) ( void * ), ldap_unbind_s ); #ifndef WIN32 DYNA_GET_FUNCTION( int( * ) ( char *, LDAPURLDesc * * ), ldap_url_parse ); DYNA_GET_FUNCTION( void( * ) ( void * ), ldap_free_urldesc ); #endif DYNA_GET_FUNCTION( int( * ) ( void *, char *, int, char *, char **, int, void ** ), ldap_search_s ); DYNA_GET_FUNCTION( void *( * )( void *, void * ), ldap_first_entry ); DYNA_GET_FUNCTION( void *( * )( void *, void * ), ldap_next_entry ); DYNA_GET_FUNCTION( char *( * )( int ), ldap_err2string ); DYNA_GET_FUNCTION( char *( * )( void *, void * ), ldap_get_dn ); DYNA_GET_FUNCTION( char *( * )( void *, void *, void ** ), ldap_first_attribute ); DYNA_GET_FUNCTION( char *( * )( void *, void *, void * ), ldap_next_attribute ); DYNA_GET_FUNCTION( char **( * )( void *, void *, const char * ), ldap_get_values ); DYNA_GET_FUNCTION( void( * ) ( char ** ), ldap_value_free ); DYNA_GET_FUNCTION( void( * ) ( void * ), ldap_memfree ); DYNA_GET_FUNCTION( void( * ) ( void *, int ), ber_free ); server = ( *ldap_init )( conn->host.name, (int)conn->port ); if ( server == NULL ) { failf( data, "LDAP local: Cannot connect to %s:%d", conn->host.name, conn->port ); status = CURLE_COULDNT_CONNECT; goto quit; } rc = ( *ldap_simple_bind_s )( server, conn->bits.user_passwd ? conn->user : NULL, conn->bits.user_passwd ? conn->passwd : NULL ); if ( rc != 0 ) { failf( data, "LDAP local: %s", ( *ldap_err2string )( rc ) ); status = CURLE_LDAP_CANNOT_BIND; goto quit; } #ifdef WIN32 rc = _ldap_url_parse( conn, &ludp ); #else rc = ( *ldap_url_parse )( data->change.url, &ludp ); #endif if ( rc != 0 ) { failf( data, "LDAP local: %s", ( *ldap_err2string )( rc ) ); status = CURLE_LDAP_INVALID_URL; goto quit; } rc = ( *ldap_search_s )( server, ludp->lud_dn, ludp->lud_scope, ludp->lud_filter, ludp->lud_attrs, 0, &result ); if ( rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED ) { failf( data, "LDAP remote: %s", ( *ldap_err2string )( rc ) ); status = CURLE_LDAP_SEARCH_FAILED; goto quit; } for ( num = 0, entryIterator = ( *ldap_first_entry )( server, result ); entryIterator; entryIterator = ( *ldap_next_entry )( server, entryIterator ), num++ ) { void *ber = NULL; /*! is really 'BerElement **' */ void *attribute; /*! suspicious that this isn't 'const' */ char *dn = ( *ldap_get_dn )( server, entryIterator ); int i; Curl_client_write( data, CLIENTWRITE_BODY, (char *)"DN: ", 4 ); Curl_client_write( data, CLIENTWRITE_BODY, (char *)dn, 0 ); Curl_client_write( data, CLIENTWRITE_BODY, (char *)"\n", 1 ); for ( attribute = ( *ldap_first_attribute )( server, entryIterator, &ber ); attribute; attribute = ( *ldap_next_attribute )( server, entryIterator, ber ) ) { char **vals = ( *ldap_get_values )( server, entryIterator, attribute ); if ( vals != NULL ) { for ( i = 0; ( vals[i] != NULL ); i++ ) { Curl_client_write( data, CLIENTWRITE_BODY, (char *)"\t", 1 ); Curl_client_write( data, CLIENTWRITE_BODY, (char*) attribute, 0 ); Curl_client_write( data, CLIENTWRITE_BODY, (char *)": ", 2 ); Curl_client_write( data, CLIENTWRITE_BODY, vals[i], 0 ); Curl_client_write( data, CLIENTWRITE_BODY, (char *)"\n", 0 ); } /* Free memory used to store values */ ( *ldap_value_free )( vals ); } Curl_client_write( data, CLIENTWRITE_BODY, (char *)"\n", 1 ); ( *ldap_memfree )( attribute ); } ( *ldap_memfree )( dn ); if ( ber ) { ( *ber_free )( ber, 0 ); } } quit: LDAP_TRACE( ( "Received %d entries\n", num ) ); if ( rc == LDAP_SIZELIMIT_EXCEEDED ) { infof( data, "There are more than %d entries\n", num ); } if ( ludp ) { ( *ldap_free_urldesc )( ludp ); } if ( server ) { ( *ldap_unbind_s )( server ); } DynaClose(); /* no data to transfer */ Curl_Transfer( conn, -1, -1, FALSE, NULL, -1, NULL ); return status; } #ifdef DEBUG_LDAP static void _ldap_trace( const char *fmt, ... ) { static int do_trace = -1; va_list args; if ( do_trace == -1 ) { const char *env = getenv( "CURL_TRACE" ); do_trace = ( env && atoi( env ) > 0 ); } if ( !do_trace ) { return; } va_start( args, fmt ); vfprintf( stderr, fmt, args ); va_end( args ); } #endif #ifdef WIN32 /* * Return scope-value for a scope-string. */ static int str2scope( const char *p ) { if ( !stricmp( p, "one" ) ) { return LDAP_SCOPE_ONELEVEL; } if ( !stricmp( p, "onetree" ) ) { return LDAP_SCOPE_ONELEVEL; } if ( !stricmp( p, "base" ) ) { return LDAP_SCOPE_BASE; } if ( !stricmp( p, "sub" ) ) { return LDAP_SCOPE_SUBTREE; } if ( !stricmp( p, "subtree" ) ) { return LDAP_SCOPE_SUBTREE; } return ( -1 ); } /* * Split 'str' into strings separated by commas. * Note: res[] points into 'str'. */ static char **split_str( char *str ) { char **res, *lasts, *s; int i; for ( i = 2, s = strchr( str,',' ); s; i++ ) s = strchr( ++s,',' ); res = calloc( i, sizeof( char* ) ); if ( !res ) { return NULL; } for ( i = 0, s = strtok_r( str, ",", &lasts ); s; s = strtok_r( NULL, ",", &lasts ), i++ ) res[i] = s; return res; } /* * Unescape the LDAP-URL components */ static bool unescape_elements( LDAPURLDesc *ludp ) { int i; if ( ludp->lud_filter ) { ludp->lud_filter = curl_unescape( ludp->lud_filter, 0 ); if ( !ludp->lud_filter ) { return ( FALSE ); } } for ( i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++ ) { ludp->lud_attrs[i] = curl_unescape( ludp->lud_attrs[i], 0 ); if ( !ludp->lud_attrs[i] ) { return ( FALSE ); } } for ( i = 0; ludp->lud_exts && ludp->lud_exts[i]; i++ ) { ludp->lud_exts[i] = curl_unescape( ludp->lud_exts[i], 0 ); if ( !ludp->lud_exts[i] ) { return ( FALSE ); } } if ( ludp->lud_dn ) { char *dn = ludp->lud_dn; char *new_dn = curl_unescape( dn, 0 ); free( dn ); if ( !new_dn ) { return ( FALSE ); } ludp->lud_dn = new_dn; } return ( TRUE ); } /* * Break apart the pieces of an LDAP URL. * Syntax: * ldap://:/???? * * already known from 'conn->host.name'. * already known from 'conn->remote_port'. * extract the rest from 'conn->path+1'. All fields are optional. e.g. * ldap://:/??? yields ludp->lud_dn = "". * * Ref. http://developer.netscape.com/docs/manuals/dirsdk/csdk30/url.htm#2831915 */ static int _ldap_url_parse2( const struct connectdata *conn, LDAPURLDesc *ludp ) { char *p, *q; int i; if ( !conn->path || conn->path[0] != '/' || !checkprefix( conn->protostr, conn->data->change.url ) ) { return LDAP_INVALID_SYNTAX; } ludp->lud_scope = LDAP_SCOPE_BASE; ludp->lud_port = conn->remote_port; ludp->lud_host = conn->host.name; /* parse DN (Distinguished Name). */ ludp->lud_dn = strdup( conn->path + 1 ); if ( !ludp->lud_dn ) { return LDAP_NO_MEMORY; } p = strchr( ludp->lud_dn, '?' ); LDAP_TRACE( ( "DN '%.*s'\n", p ? (size_t)( p - ludp->lud_dn ) : strlen( ludp->lud_dn ), ludp->lud_dn ) ); if ( !p ) { goto success; } *p++ = '\0'; /* parse attributes. skip "??". */ q = strchr( p, '?' ); if ( q ) { *q++ = '\0'; } if ( *p && *p != '?' ) { ludp->lud_attrs = split_str( p ); if ( !ludp->lud_attrs ) { return LDAP_NO_MEMORY; } for ( i = 0; ludp->lud_attrs[i]; i++ ) LDAP_TRACE( ( "attr[%d] '%s'\n", i, ludp->lud_attrs[i] ) ); } p = q; if ( !p ) { goto success; } /* parse scope. skip "??" */ q = strchr( p, '?' ); if ( q ) { *q++ = '\0'; } if ( *p && *p != '?' ) { ludp->lud_scope = str2scope( p ); if ( ludp->lud_scope == -1 ) { return LDAP_INVALID_SYNTAX; } LDAP_TRACE( ( "scope %d\n", ludp->lud_scope ) ); } p = q; if ( !p ) { goto success; } /* parse filter */ q = strchr( p, '?' ); if ( q ) { *q++ = '\0'; } if ( !*p ) { return LDAP_INVALID_SYNTAX; } ludp->lud_filter = p; LDAP_TRACE( ( "filter '%s'\n", ludp->lud_filter ) ); p = q; if ( !p ) { goto success; } /* parse extensions */ ludp->lud_exts = split_str( p ); if ( !ludp->lud_exts ) { return LDAP_NO_MEMORY; } for ( i = 0; ludp->lud_exts[i]; i++ ) LDAP_TRACE( ( "exts[%d] '%s'\n", i, ludp->lud_exts[i] ) ); success: if ( !unescape_elements( ludp ) ) { return LDAP_NO_MEMORY; } return LDAP_SUCCESS; } static int _ldap_url_parse( const struct connectdata *conn, LDAPURLDesc **ludpp ) { LDAPURLDesc *ludp = calloc( sizeof( *ludp ), 1 ); int rc; *ludpp = NULL; if ( !ludp ) { return LDAP_NO_MEMORY; } rc = _ldap_url_parse2( conn, ludp ); if ( rc != LDAP_SUCCESS ) { _ldap_free_urldesc( ludp ); ludp = NULL; } *ludpp = ludp; return ( rc ); } static void _ldap_free_urldesc( LDAPURLDesc *ludp ) { int i; if ( !ludp ) { return; } if ( ludp->lud_dn ) { free( ludp->lud_dn ); } if ( ludp->lud_filter ) { free( ludp->lud_filter ); } if ( ludp->lud_attrs ) { for ( i = 0; ludp->lud_attrs[i]; i++ ) free( ludp->lud_attrs[i] ); free( ludp->lud_attrs ); } if ( ludp->lud_exts ) { for ( i = 0; ludp->lud_exts[i]; i++ ) free( ludp->lud_exts[i] ); free( ludp->lud_exts ); } free( ludp ); } #endif /* WIN32 */ #endif /* CURL_DISABLE_LDAP */