Skip to content
Permalink
OPENLDAP_REL_E…
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
609 lines (510 sloc) 14.5 KB
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2018 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
* All rights reserved.
*/
#include "portable.h"
#include <stdio.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <ac/stdlib.h>
#include <ac/param.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>
#include <ac/unistd.h>
#include "ldap-int.h"
#include "ldap_log.h"
/* Caller must hold the conn_mutex since simultaneous accesses are possible */
int ldap_open_defconn( LDAP *ld )
{
ld->ld_defconn = ldap_new_connection( ld,
&ld->ld_options.ldo_defludp, 1, 1, NULL, 0, 0 );
if( ld->ld_defconn == NULL ) {
ld->ld_errno = LDAP_SERVER_DOWN;
return -1;
}
++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
return 0;
}
/*
* ldap_open - initialize and connect to an ldap server. A magic cookie to
* be used for future communication is returned on success, NULL on failure.
* "host" may be a space-separated list of hosts or IP addresses
*
* Example:
* LDAP *ld;
* ld = ldap_open( hostname, port );
*/
LDAP *
ldap_open( LDAP_CONST char *host, int port )
{
int rc;
LDAP *ld;
Debug( LDAP_DEBUG_TRACE, "ldap_open(%s, %d)\n",
host, port, 0 );
ld = ldap_init( host, port );
if ( ld == NULL ) {
return( NULL );
}
LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
rc = ldap_open_defconn( ld );
LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
if( rc < 0 ) {
ldap_ld_free( ld, 0, NULL, NULL );
ld = NULL;
}
Debug( LDAP_DEBUG_TRACE, "ldap_open: %s\n",
ld != NULL ? "succeeded" : "failed", 0, 0 );
return ld;
}
int
ldap_create( LDAP **ldp )
{
LDAP *ld;
struct ldapoptions *gopts;
*ldp = NULL;
/* Get pointer to global option structure */
if ( (gopts = LDAP_INT_GLOBAL_OPT()) == NULL) {
return LDAP_NO_MEMORY;
}
/* Initialize the global options, if not already done. */
if( gopts->ldo_valid != LDAP_INITIALIZED ) {
ldap_int_initialize(gopts, NULL);
if ( gopts->ldo_valid != LDAP_INITIALIZED )
return LDAP_LOCAL_ERROR;
}
Debug( LDAP_DEBUG_TRACE, "ldap_create\n", 0, 0, 0 );
if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) {
return( LDAP_NO_MEMORY );
}
if ( (ld->ldc = (struct ldap_common *) LDAP_CALLOC( 1,
sizeof(struct ldap_common) )) == NULL ) {
LDAP_FREE( (char *)ld );
return( LDAP_NO_MEMORY );
}
/* copy the global options */
LDAP_MUTEX_LOCK( &gopts->ldo_mutex );
AC_MEMCPY(&ld->ld_options, gopts, sizeof(ld->ld_options));
#ifdef LDAP_R_COMPILE
/* Properly initialize the structs mutex */
ldap_pvt_thread_mutex_init( &(ld->ld_ldopts_mutex) );
#endif
LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex );
ld->ld_valid = LDAP_VALID_SESSION;
/* but not pointers to malloc'ed items */
ld->ld_options.ldo_sctrls = NULL;
ld->ld_options.ldo_cctrls = NULL;
ld->ld_options.ldo_defludp = NULL;
ld->ld_options.ldo_conn_cbs = NULL;
#ifdef HAVE_CYRUS_SASL
ld->ld_options.ldo_def_sasl_mech = gopts->ldo_def_sasl_mech
? LDAP_STRDUP( gopts->ldo_def_sasl_mech ) : NULL;
ld->ld_options.ldo_def_sasl_realm = gopts->ldo_def_sasl_realm
? LDAP_STRDUP( gopts->ldo_def_sasl_realm ) : NULL;
ld->ld_options.ldo_def_sasl_authcid = gopts->ldo_def_sasl_authcid
? LDAP_STRDUP( gopts->ldo_def_sasl_authcid ) : NULL;
ld->ld_options.ldo_def_sasl_authzid = gopts->ldo_def_sasl_authzid
? LDAP_STRDUP( gopts->ldo_def_sasl_authzid ) : NULL;
#endif
#ifdef HAVE_TLS
/* We explicitly inherit the SSL_CTX, don't need the names/paths. Leave
* them empty to allow new SSL_CTX's to be created from scratch.
*/
memset( &ld->ld_options.ldo_tls_info, 0,
sizeof( ld->ld_options.ldo_tls_info ));
ld->ld_options.ldo_tls_ctx = NULL;
#endif
if ( gopts->ldo_defludp ) {
ld->ld_options.ldo_defludp = ldap_url_duplist(gopts->ldo_defludp);
if ( ld->ld_options.ldo_defludp == NULL ) goto nomem;
}
if (( ld->ld_selectinfo = ldap_new_select_info()) == NULL ) goto nomem;
ld->ld_lberoptions = LBER_USE_DER;
ld->ld_sb = ber_sockbuf_alloc( );
if ( ld->ld_sb == NULL ) goto nomem;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_init( &ld->ld_msgid_mutex );
ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex );
ldap_pvt_thread_mutex_init( &ld->ld_req_mutex );
ldap_pvt_thread_mutex_init( &ld->ld_res_mutex );
ldap_pvt_thread_mutex_init( &ld->ld_abandon_mutex );
ldap_pvt_thread_mutex_init( &ld->ld_ldcmutex );
#endif
ld->ld_ldcrefcnt = 1;
*ldp = ld;
return LDAP_SUCCESS;
nomem:
ldap_free_select_info( ld->ld_selectinfo );
ldap_free_urllist( ld->ld_options.ldo_defludp );
#ifdef HAVE_CYRUS_SASL
LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid );
LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid );
LDAP_FREE( ld->ld_options.ldo_def_sasl_realm );
LDAP_FREE( ld->ld_options.ldo_def_sasl_mech );
#endif
LDAP_FREE( (char *)ld );
return LDAP_NO_MEMORY;
}
/*
* ldap_init - initialize the LDAP library. A magic cookie to be used for
* future communication is returned on success, NULL on failure.
* "host" may be a space-separated list of hosts or IP addresses
*
* Example:
* LDAP *ld;
* ld = ldap_init( host, port );
*/
LDAP *
ldap_init( LDAP_CONST char *defhost, int defport )
{
LDAP *ld;
int rc;
rc = ldap_create(&ld);
if ( rc != LDAP_SUCCESS )
return NULL;
if (defport != 0)
ld->ld_options.ldo_defport = defport;
if (defhost != NULL) {
rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, defhost);
if ( rc != LDAP_SUCCESS ) {
ldap_ld_free(ld, 1, NULL, NULL);
return NULL;
}
}
return( ld );
}
int
ldap_initialize( LDAP **ldp, LDAP_CONST char *url )
{
int rc;
LDAP *ld;
*ldp = NULL;
rc = ldap_create(&ld);
if ( rc != LDAP_SUCCESS )
return rc;
if (url != NULL) {
rc = ldap_set_option(ld, LDAP_OPT_URI, url);
if ( rc != LDAP_SUCCESS ) {
ldap_ld_free(ld, 1, NULL, NULL);
return rc;
}
#ifdef LDAP_CONNECTIONLESS
if (ldap_is_ldapc_url(url))
LDAP_IS_UDP(ld) = 1;
#endif
}
*ldp = ld;
return LDAP_SUCCESS;
}
int
ldap_init_fd(
ber_socket_t fd,
int proto,
LDAP_CONST char *url,
LDAP **ldp
)
{
int rc;
LDAP *ld;
LDAPConn *conn;
#ifdef LDAP_CONNECTIONLESS
ber_socklen_t len;
#endif
*ldp = NULL;
rc = ldap_create( &ld );
if( rc != LDAP_SUCCESS )
return( rc );
if (url != NULL) {
rc = ldap_set_option(ld, LDAP_OPT_URI, url);
if ( rc != LDAP_SUCCESS ) {
ldap_ld_free(ld, 1, NULL, NULL);
return rc;
}
}
LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
/* Attach the passed socket as the LDAP's connection */
conn = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 );
if( conn == NULL ) {
ldap_unbind_ext( ld, NULL, NULL );
return( LDAP_NO_MEMORY );
}
if( url )
conn->lconn_server = ldap_url_dup( ld->ld_options.ldo_defludp );
ber_sockbuf_ctrl( conn->lconn_sb, LBER_SB_OPT_SET_FD, &fd );
ld->ld_defconn = conn;
++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
switch( proto ) {
case LDAP_PROTO_TCP:
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
#endif
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
break;
#ifdef LDAP_CONNECTIONLESS
case LDAP_PROTO_UDP:
LDAP_IS_UDP(ld) = 1;
if( ld->ld_options.ldo_peer )
ldap_memfree( ld->ld_options.ldo_peer );
ld->ld_options.ldo_peer = ldap_memcalloc( 1, sizeof( struct sockaddr_storage ) );
len = sizeof( struct sockaddr_storage );
if( getpeername ( fd, ld->ld_options.ldo_peer, &len ) < 0) {
ldap_unbind_ext( ld, NULL, NULL );
return( AC_SOCKET_ERROR );
}
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" );
#endif
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
break;
#endif /* LDAP_CONNECTIONLESS */
case LDAP_PROTO_IPC:
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
#endif
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
break;
case LDAP_PROTO_EXT:
/* caller must supply sockbuf handlers */
break;
default:
ldap_unbind_ext( ld, NULL, NULL );
return LDAP_PARAM_ERROR;
}
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
INT_MAX, (void *)"ldap_" );
#endif
/* Add the connection to the *LDAP's select pool */
ldap_mark_select_read( ld, conn->lconn_sb );
*ldp = ld;
return LDAP_SUCCESS;
}
/* Protected by ld_conn_mutex */
int
ldap_int_open_connection(
LDAP *ld,
LDAPConn *conn,
LDAPURLDesc *srv,
int async )
{
int rc = -1;
int proto;
Debug( LDAP_DEBUG_TRACE, "ldap_int_open_connection\n", 0, 0, 0 );
switch ( proto = ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) {
case LDAP_PROTO_TCP:
rc = ldap_connect_to_host( ld, conn->lconn_sb,
proto, srv, async );
if ( rc == -1 ) return rc;
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
#endif
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
break;
#ifdef LDAP_CONNECTIONLESS
case LDAP_PROTO_UDP:
LDAP_IS_UDP(ld) = 1;
rc = ldap_connect_to_host( ld, conn->lconn_sb,
proto, srv, async );
if ( rc == -1 ) return rc;
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" );
#endif
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
break;
#endif
case LDAP_PROTO_IPC:
#ifdef LDAP_PF_LOCAL
/* only IPC mechanism supported is PF_LOCAL (PF_UNIX) */
rc = ldap_connect_to_path( ld, conn->lconn_sb,
srv, async );
if ( rc == -1 ) return rc;
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
#endif
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
break;
#endif /* LDAP_PF_LOCAL */
default:
return -1;
break;
}
conn->lconn_created = time( NULL );
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
INT_MAX, (void *)"ldap_" );
#endif
#ifdef LDAP_CONNECTIONLESS
if( proto == LDAP_PROTO_UDP ) return 0;
#endif
#ifdef HAVE_TLS
if (rc == 0 && ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD ||
strcmp( srv->lud_scheme, "ldaps" ) == 0 ))
{
++conn->lconn_refcnt; /* avoid premature free */
rc = ldap_int_tls_start( ld, conn, srv );
--conn->lconn_refcnt;
if (rc != LDAP_SUCCESS) {
/* process connection callbacks */
{
struct ldapoptions *lo;
ldaplist *ll;
ldap_conncb *cb;
lo = &ld->ld_options;
LDAP_MUTEX_LOCK( &lo->ldo_mutex );
if ( lo->ldo_conn_cbs ) {
for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
cb = ll->ll_data;
cb->lc_del( ld, conn->lconn_sb, cb );
}
}
LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
lo = LDAP_INT_GLOBAL_OPT();
LDAP_MUTEX_LOCK( &lo->ldo_mutex );
if ( lo->ldo_conn_cbs ) {
for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
cb = ll->ll_data;
cb->lc_del( ld, conn->lconn_sb, cb );
}
}
LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
}
return -1;
}
}
#endif
return( 0 );
}
/*
* ldap_open_internal_connection - open connection and set file descriptor
*
* note: ldap_init_fd() may be preferable
*/
int
ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp )
{
int rc;
LDAPConn *c;
LDAPRequest *lr;
LDAP *ld;
rc = ldap_create( &ld );
if( rc != LDAP_SUCCESS ) {
*ldp = NULL;
return( rc );
}
/* Make it appear that a search request, msgid 0, was sent */
lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ));
if( lr == NULL ) {
ldap_unbind_ext( ld, NULL, NULL );
*ldp = NULL;
return( LDAP_NO_MEMORY );
}
memset(lr, 0, sizeof( LDAPRequest ));
lr->lr_msgid = 0;
lr->lr_status = LDAP_REQST_INPROGRESS;
lr->lr_res_errno = LDAP_SUCCESS;
/* no mutex lock needed, we just created this ld here */
ld->ld_requests = lr;
LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
/* Attach the passed socket as the *LDAP's connection */
c = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 );
if( c == NULL ) {
ldap_unbind_ext( ld, NULL, NULL );
*ldp = NULL;
LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
return( LDAP_NO_MEMORY );
}
ber_sockbuf_ctrl( c->lconn_sb, LBER_SB_OPT_SET_FD, fdp );
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_debug,
LBER_SBIOD_LEVEL_PROVIDER, (void *)"int_" );
#endif
ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_tcp,
LBER_SBIOD_LEVEL_PROVIDER, NULL );
ld->ld_defconn = c;
LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
/* Add the connection to the *LDAP's select pool */
ldap_mark_select_read( ld, c->lconn_sb );
/* Make this connection an LDAP V3 protocol connection */
rc = LDAP_VERSION3;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &rc );
*ldp = ld;
++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
return( LDAP_SUCCESS );
}
LDAP *
ldap_dup( LDAP *old )
{
LDAP *ld;
if ( old == NULL ) {
return( NULL );
}
Debug( LDAP_DEBUG_TRACE, "ldap_dup\n", 0, 0, 0 );
if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) {
return( NULL );
}
LDAP_MUTEX_LOCK( &old->ld_ldcmutex );
ld->ldc = old->ldc;
old->ld_ldcrefcnt++;
LDAP_MUTEX_UNLOCK( &old->ld_ldcmutex );
return ( ld );
}
int
ldap_int_check_async_open( LDAP *ld, ber_socket_t sd )
{
struct timeval tv = { 0 };
int rc;
rc = ldap_int_poll( ld, sd, &tv, 1 );
switch ( rc ) {
case 0:
/* now ready to start tls */
ld->ld_defconn->lconn_status = LDAP_CONNST_CONNECTED;
break;
default:
ld->ld_errno = LDAP_CONNECT_ERROR;
return -1;
case -2:
/* connect not completed yet */
ld->ld_errno = LDAP_X_CONNECTING;
return rc;
}
#ifdef HAVE_TLS
if ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD ||
!strcmp( ld->ld_defconn->lconn_server->lud_scheme, "ldaps" )) {
++ld->ld_defconn->lconn_refcnt; /* avoid premature free */
rc = ldap_int_tls_start( ld, ld->ld_defconn, ld->ld_defconn->lconn_server );
--ld->ld_defconn->lconn_refcnt;
}
#endif
return rc;
}