2004-11-06 23:15:39 +03:00
/*
Unix SMB / CIFS mplementation .
LDAP protocol helper functions for SAMBA
Copyright ( C ) Andrew Tridgell 2004
Copyright ( C ) Volker Lendecke 2004
Copyright ( C ) Stefan Metzmacher 2004
Copyright ( C ) Simo Sorce 2004
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2006-01-03 18:40:05 +03:00
# include "libcli/util/asn_1.h"
2004-11-06 23:15:39 +03:00
# include "dlinklist.h"
2005-06-16 09:39:40 +04:00
# include "lib/events/events.h"
# include "lib/socket/socket.h"
2006-01-06 07:01:23 +03:00
# include "libcli/ldap/ldap.h"
2005-06-16 09:39:40 +04:00
# include "libcli/ldap/ldap_client.h"
2005-10-24 13:34:12 +04:00
# include "libcli/composite/composite.h"
2005-11-10 03:28:02 +03:00
# include "lib/stream/packet.h"
2005-12-28 18:38:36 +03:00
# include "auth/gensec/gensec.h"
2006-04-25 15:50:32 +04:00
# include "system/time.h"
2004-11-06 23:15:39 +03:00
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
/*
create a new ldap_connection stucture . The event context is optional
*/
struct ldap_connection * ldap_new_connection ( TALLOC_CTX * mem_ctx ,
struct event_context * ev )
2005-01-30 10:22:16 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_connection * conn ;
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
conn = talloc_zero ( mem_ctx , struct ldap_connection ) ;
if ( conn = = NULL ) {
return NULL ;
2005-01-30 10:22:16 +03:00
}
2005-06-16 09:39:40 +04:00
if ( ev = = NULL ) {
ev = event_context_init ( conn ) ;
if ( ev = = NULL ) {
talloc_free ( conn ) ;
return NULL ;
2005-01-30 10:22:16 +03:00
}
}
2005-06-16 09:39:40 +04:00
conn - > next_messageid = 1 ;
conn - > event . event_ctx = ev ;
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
/* set a reasonable request timeout */
conn - > timeout = 60 ;
2005-01-30 10:22:16 +03:00
2006-04-25 15:50:32 +04:00
/* explicitly avoid reconnections by default */
conn - > reconnect . max_retries = 0 ;
2005-06-16 09:39:40 +04:00
return conn ;
2005-01-30 10:22:16 +03:00
}
2005-06-16 09:39:40 +04:00
/*
the connection is dead
*/
static void ldap_connection_dead ( struct ldap_connection * conn )
{
struct ldap_request * req ;
2005-01-30 10:22:16 +03:00
2006-04-25 15:50:32 +04:00
/* return an error for any pending request ... */
2005-06-16 09:39:40 +04:00
while ( conn - > pending ) {
req = conn - > pending ;
DLIST_REMOVE ( req - > conn - > pending , req ) ;
req - > state = LDAP_REQUEST_DONE ;
req - > status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
}
2005-06-20 05:17:29 +04:00
talloc_free ( conn - > tls ) ;
2006-04-26 20:52:45 +04:00
/* talloc_free(conn->sock); this will also free event.fde */
2006-04-25 15:50:32 +04:00
talloc_free ( conn - > packet ) ;
2005-06-20 05:17:29 +04:00
conn - > tls = NULL ;
2006-04-25 15:50:32 +04:00
conn - > sock = NULL ;
conn - > event . fde = NULL ;
conn - > packet = NULL ;
2005-01-30 10:22:16 +03:00
}
2006-04-25 15:50:32 +04:00
static void ldap_reconnect ( struct ldap_connection * conn ) ;
2005-11-10 03:28:02 +03:00
/*
handle packet errors
*/
2006-02-22 04:31:35 +03:00
static void ldap_error_handler ( void * private_data , NTSTATUS status )
2005-11-10 03:28:02 +03:00
{
2006-02-22 04:31:35 +03:00
struct ldap_connection * conn = talloc_get_type ( private_data ,
2005-11-10 03:28:02 +03:00
struct ldap_connection ) ;
ldap_connection_dead ( conn ) ;
2006-04-25 15:50:32 +04:00
/* but try to reconnect so that the ldb client can go on */
ldap_reconnect ( conn ) ;
2005-11-10 03:28:02 +03:00
}
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
/*
match up with a pending message , adding to the replies list
*/
static void ldap_match_message ( struct ldap_connection * conn , struct ldap_message * msg )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_request * req ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
for ( req = conn - > pending ; req ; req = req - > next ) {
if ( req - > messageid = = msg - > messageid ) break ;
2004-11-06 23:15:39 +03:00
}
2005-07-17 14:52:31 +04:00
/* match a zero message id to the last request sent.
It seems that servers send 0 if unable to parse */
if ( req = = NULL & & msg - > messageid = = 0 ) {
req = conn - > pending ;
}
2005-06-16 09:39:40 +04:00
if ( req = = NULL ) {
DEBUG ( 0 , ( " ldap: no matching message id for %u \n " ,
msg - > messageid ) ) ;
talloc_free ( msg ) ;
return ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/* add to the list of replies received */
talloc_steal ( req , msg ) ;
req - > replies = talloc_realloc ( req , req - > replies ,
struct ldap_message * , req - > num_replies + 1 ) ;
if ( req - > replies = = NULL ) {
req - > status = NT_STATUS_NO_MEMORY ;
req - > state = LDAP_REQUEST_DONE ;
DLIST_REMOVE ( conn - > pending , req ) ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
return ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
req - > replies [ req - > num_replies ] = talloc_steal ( req - > replies , msg ) ;
req - > num_replies + + ;
2004-11-06 23:15:39 +03:00
2005-06-21 17:42:47 +04:00
if ( msg - > type ! = LDAP_TAG_SearchResultEntry & &
msg - > type ! = LDAP_TAG_SearchResultReference ) {
2005-06-16 09:39:40 +04:00
/* currently only search results expect multiple
replies */
req - > state = LDAP_REQUEST_DONE ;
DLIST_REMOVE ( conn - > pending , req ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
2004-11-06 23:15:39 +03:00
}
2005-11-10 03:28:02 +03:00
2005-06-16 09:39:40 +04:00
/*
2005-11-10 03:28:02 +03:00
check if a blob is a complete ldap packet
handle wrapper or unwrapped connections
2005-06-16 09:39:40 +04:00
*/
2006-02-22 04:31:35 +03:00
NTSTATUS ldap_complete_packet ( void * private_data , DATA_BLOB blob , size_t * size )
2004-11-06 23:15:39 +03:00
{
2006-02-22 04:31:35 +03:00
struct ldap_connection * conn = talloc_get_type ( private_data ,
2005-11-10 03:28:02 +03:00
struct ldap_connection ) ;
if ( conn - > enable_wrap ) {
2006-02-22 04:31:35 +03:00
return packet_full_request_u32 ( private_data , blob , size ) ;
2004-11-06 23:15:39 +03:00
}
2006-02-22 04:31:35 +03:00
return ldap_full_packet ( private_data , blob , size ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
2005-11-10 03:28:02 +03:00
decode / process plain data
2005-06-16 09:39:40 +04:00
*/
2005-11-10 03:28:02 +03:00
static NTSTATUS ldap_decode_plain ( struct ldap_connection * conn , DATA_BLOB blob )
2004-11-06 23:15:39 +03:00
{
2005-11-10 03:28:02 +03:00
struct asn1_data asn1 ;
struct ldap_message * msg = talloc ( conn , struct ldap_message ) ;
2004-11-06 23:15:39 +03:00
2005-11-10 03:28:02 +03:00
if ( msg = = NULL ) {
return NT_STATUS_LDAP ( LDAP_PROTOCOL_ERROR ) ;
}
2004-11-06 23:15:39 +03:00
2005-11-10 03:28:02 +03:00
if ( ! asn1_load ( & asn1 , blob ) ) {
return NT_STATUS_LDAP ( LDAP_PROTOCOL_ERROR ) ;
}
if ( ! ldap_decode ( & asn1 , msg ) ) {
return NT_STATUS_LDAP ( LDAP_PROTOCOL_ERROR ) ;
}
2004-11-06 23:15:39 +03:00
2005-11-10 03:28:02 +03:00
ldap_match_message ( conn , msg ) ;
2004-11-06 23:15:39 +03:00
2005-11-10 03:28:02 +03:00
data_blob_free ( & blob ) ;
asn1_free ( & asn1 ) ;
return NT_STATUS_OK ;
2004-11-06 23:15:39 +03:00
}
2005-05-11 18:38:13 +04:00
/*
2005-11-10 03:28:02 +03:00
decode / process wrapped data
2005-05-11 18:38:13 +04:00
*/
2005-11-10 03:28:02 +03:00
static NTSTATUS ldap_decode_wrapped ( struct ldap_connection * conn , DATA_BLOB blob )
2005-05-11 18:38:13 +04:00
{
2005-11-10 03:28:02 +03:00
DATA_BLOB wrapped , unwrapped ;
struct asn1_data asn1 ;
struct ldap_message * msg = talloc ( conn , struct ldap_message ) ;
2005-06-16 09:39:40 +04:00
NTSTATUS status ;
2005-05-11 18:38:13 +04:00
2005-11-10 03:28:02 +03:00
if ( msg = = NULL ) {
return NT_STATUS_LDAP ( LDAP_PROTOCOL_ERROR ) ;
2005-06-16 09:39:40 +04:00
}
2005-05-11 18:38:13 +04:00
2005-11-10 03:28:02 +03:00
wrapped = data_blob_const ( blob . data + 4 , blob . length - 4 ) ;
2005-05-11 18:38:13 +04:00
2005-11-10 03:28:02 +03:00
status = gensec_unwrap ( conn - > gensec , msg , & wrapped , & unwrapped ) ;
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-11-10 03:28:02 +03:00
return NT_STATUS_LDAP ( LDAP_PROTOCOL_ERROR ) ;
2005-06-16 09:39:40 +04:00
}
2005-05-11 18:38:13 +04:00
2005-11-10 03:28:02 +03:00
data_blob_free ( & blob ) ;
if ( ! asn1_load ( & asn1 , unwrapped ) ) {
return NT_STATUS_LDAP ( LDAP_PROTOCOL_ERROR ) ;
}
while ( ldap_decode ( & asn1 , msg ) ) {
ldap_match_message ( conn , msg ) ;
msg = talloc ( conn , struct ldap_message ) ;
2005-06-16 09:39:40 +04:00
}
2005-11-10 03:28:02 +03:00
talloc_free ( msg ) ;
asn1_free ( & asn1 ) ;
return NT_STATUS_OK ;
2005-05-11 18:38:13 +04:00
}
/*
2005-11-10 03:28:02 +03:00
handle ldap recv events
2005-05-11 18:38:13 +04:00
*/
2006-02-22 04:31:35 +03:00
static NTSTATUS ldap_recv_handler ( void * private_data , DATA_BLOB blob )
2005-05-11 18:38:13 +04:00
{
2006-02-22 04:31:35 +03:00
struct ldap_connection * conn = talloc_get_type ( private_data ,
2005-11-10 03:28:02 +03:00
struct ldap_connection ) ;
if ( conn - > enable_wrap ) {
return ldap_decode_wrapped ( conn , blob ) ;
2005-05-11 18:38:13 +04:00
}
2005-11-10 03:28:02 +03:00
return ldap_decode_plain ( conn , blob ) ;
2005-05-11 18:38:13 +04:00
}
2005-06-16 09:39:40 +04:00
/*
handle ldap socket events
*/
static void ldap_io_handler ( struct event_context * ev , struct fd_event * fde ,
2006-02-22 04:31:35 +03:00
uint16_t flags , void * private_data )
2004-11-06 23:15:39 +03:00
{
2006-02-22 04:31:35 +03:00
struct ldap_connection * conn = talloc_get_type ( private_data ,
2005-11-10 03:28:02 +03:00
struct ldap_connection ) ;
2005-06-16 09:39:40 +04:00
if ( flags & EVENT_FD_WRITE ) {
2005-11-10 03:28:02 +03:00
packet_queue_run ( conn - > packet ) ;
2005-06-20 05:17:29 +04:00
if ( conn - > tls = = NULL ) return ;
2005-06-16 09:39:40 +04:00
}
if ( flags & EVENT_FD_READ ) {
2005-11-10 03:28:02 +03:00
packet_recv ( conn - > packet ) ;
2005-05-11 18:38:13 +04:00
}
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
parse a ldap URL
*/
static NTSTATUS ldap_parse_basic_url ( TALLOC_CTX * mem_ctx , const char * url ,
char * * host , uint16_t * port , BOOL * ldaps )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
int tmp_port = 0 ;
char protocol [ 11 ] ;
char tmp_host [ 255 ] ;
const char * p = url ;
int ret ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* skip leading "URL:" (if any) */
if ( strncasecmp ( p , " URL: " , 4 ) = = 0 ) {
p + = 4 ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* Paranoia check */
SMB_ASSERT ( sizeof ( protocol ) > 10 & & sizeof ( tmp_host ) > 254 ) ;
ret = sscanf ( p , " %10[^:]://%254[^:/]:%d " , protocol , tmp_host , & tmp_port ) ;
if ( ret < 2 ) {
return NT_STATUS_INVALID_PARAMETER ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( strequal ( protocol , " ldap " ) ) {
* port = 389 ;
* ldaps = False ;
} else if ( strequal ( protocol , " ldaps " ) ) {
* port = 636 ;
* ldaps = True ;
2004-11-06 23:15:39 +03:00
} else {
2005-06-16 09:39:40 +04:00
DEBUG ( 0 , ( " unrecognised ldap protocol (%s)! \n " , protocol ) ) ;
return NT_STATUS_PROTOCOL_UNREACHABLE ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( tmp_port ! = 0 )
* port = tmp_port ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
* host = talloc_strdup ( mem_ctx , tmp_host ) ;
NT_STATUS_HAVE_NO_MEMORY ( * host ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
return NT_STATUS_OK ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
connect to a ldap server
*/
2004-11-06 23:15:39 +03:00
2005-10-24 13:34:12 +04:00
struct ldap_connect_state {
struct composite_context * ctx ;
struct ldap_connection * conn ;
} ;
2004-11-06 23:15:39 +03:00
2005-10-24 13:34:12 +04:00
static void ldap_connect_recv_conn ( struct composite_context * ctx ) ;
2004-11-06 23:15:39 +03:00
2005-10-24 13:34:12 +04:00
struct composite_context * ldap_connect_send ( struct ldap_connection * conn ,
const char * url )
{
struct composite_context * result , * ctx ;
struct ldap_connect_state * state ;
2006-04-25 15:50:32 +04:00
if ( conn - > reconnect . url = = NULL ) {
conn - > reconnect . url = talloc_strdup ( conn , url ) ;
if ( conn - > reconnect . url = = NULL ) goto failed ;
}
2005-10-24 13:34:12 +04:00
result = talloc_zero ( NULL , struct composite_context ) ;
if ( result = = NULL ) goto failed ;
result - > state = COMPOSITE_STATE_IN_PROGRESS ;
result - > async . fn = NULL ;
result - > event_ctx = conn - > event . event_ctx ;
state = talloc ( result , struct ldap_connect_state ) ;
if ( state = = NULL ) goto failed ;
state - > ctx = result ;
result - > private_data = state ;
state - > conn = conn ;
state - > ctx - > status = ldap_parse_basic_url ( conn , url , & conn - > host ,
& conn - > port , & conn - > ldaps ) ;
if ( ! NT_STATUS_IS_OK ( state - > ctx - > status ) ) {
2005-12-08 04:13:45 +03:00
composite_error ( state - > ctx , state - > ctx - > status ) ;
2005-10-24 13:34:12 +04:00
return result ;
}
2004-11-06 23:15:39 +03:00
2005-10-28 15:02:42 +04:00
ctx = socket_connect_multi_send ( state , conn - > host , 1 , & conn - > port ,
conn - > event . event_ctx ) ;
2005-10-24 13:34:12 +04:00
if ( ctx = = NULL ) goto failed ;
ctx - > async . fn = ldap_connect_recv_conn ;
ctx - > async . private_data = state ;
return result ;
failed :
talloc_free ( result ) ;
return NULL ;
}
static void ldap_connect_recv_conn ( struct composite_context * ctx )
{
struct ldap_connect_state * state =
talloc_get_type ( ctx - > async . private_data ,
struct ldap_connect_state ) ;
struct ldap_connection * conn = state - > conn ;
2005-10-28 15:02:42 +04:00
uint16_t port ;
2005-10-24 13:34:12 +04:00
2005-10-28 15:02:42 +04:00
state - > ctx - > status = socket_connect_multi_recv ( ctx , state , & conn - > sock ,
& port ) ;
2005-10-24 13:34:12 +04:00
if ( ! composite_is_ok ( state - > ctx ) ) return ;
2005-06-16 09:39:40 +04:00
/* setup a handler for events on this socket */
conn - > event . fde = event_add_fd ( conn - > event . event_ctx , conn - > sock ,
socket_get_fd ( conn - > sock ) ,
EVENT_FD_READ , ldap_io_handler , conn ) ;
if ( conn - > event . fde = = NULL ) {
2005-10-24 13:34:12 +04:00
composite_error ( state - > ctx , NT_STATUS_INTERNAL_ERROR ) ;
return ;
2004-11-06 23:15:39 +03:00
}
2005-06-20 05:17:29 +04:00
conn - > tls = tls_init_client ( conn - > sock , conn - > event . fde , conn - > ldaps ) ;
if ( conn - > tls = = NULL ) {
talloc_free ( conn - > sock ) ;
2005-10-24 13:34:12 +04:00
return ;
2005-06-20 05:17:29 +04:00
}
talloc_steal ( conn , conn - > tls ) ;
talloc_steal ( conn - > tls , conn - > sock ) ;
2005-11-10 03:28:02 +03:00
conn - > packet = packet_init ( conn ) ;
if ( conn - > packet = = NULL ) {
talloc_free ( conn - > sock ) ;
return ;
}
packet_set_private ( conn - > packet , conn ) ;
packet_set_tls ( conn - > packet , conn - > tls ) ;
packet_set_callback ( conn - > packet , ldap_recv_handler ) ;
packet_set_full_request ( conn - > packet , ldap_complete_packet ) ;
packet_set_error_handler ( conn - > packet , ldap_error_handler ) ;
packet_set_event_context ( conn - > packet , conn - > event . event_ctx ) ;
2005-11-14 06:45:57 +03:00
packet_set_fde ( conn - > packet , conn - > event . fde ) ;
packet_set_serialise ( conn - > packet ) ;
2005-11-10 03:28:02 +03:00
2005-10-24 13:34:12 +04:00
composite_done ( state - > ctx ) ;
return ;
}
NTSTATUS ldap_connect_recv ( struct composite_context * ctx )
{
NTSTATUS status = composite_wait ( ctx ) ;
talloc_free ( ctx ) ;
return status ;
}
NTSTATUS ldap_connect ( struct ldap_connection * conn , const char * url )
{
struct composite_context * ctx = ldap_connect_send ( conn , url ) ;
return ldap_connect_recv ( ctx ) ;
2004-11-06 23:15:39 +03:00
}
2006-04-25 16:34:13 +04:00
/* set reconnect parameters */
void ldap_set_reconn_params ( struct ldap_connection * conn , int max_retries )
{
if ( conn ) {
conn - > reconnect . max_retries = max_retries ;
conn - > reconnect . retries = 0 ;
conn - > reconnect . previous = time ( NULL ) ;
}
}
2006-04-25 15:50:32 +04:00
/* Actually this function is NOT ASYNC safe, FIXME? */
static void ldap_reconnect ( struct ldap_connection * conn )
{
NTSTATUS status ;
time_t now = time ( NULL ) ;
/* do we have set up reconnect ? */
if ( conn - > reconnect . max_retries = = 0 ) return ;
/* is the retry time expired ? */
if ( now > conn - > reconnect . previous + 30 ) {
conn - > reconnect . retries = 0 ;
conn - > reconnect . previous = now ;
}
/* are we reconnectind too often and too fast? */
if ( conn - > reconnect . retries > conn - > reconnect . max_retries ) return ;
/* keep track of the number of reconnections */
conn - > reconnect . retries + + ;
/* reconnect */
status = ldap_connect ( conn , conn - > reconnect . url ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return ;
}
/* rebind */
status = ldap_rebind ( conn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldap_connection_dead ( conn ) ;
}
}
2005-06-16 09:39:40 +04:00
/* destroy an open ldap request */
static int ldap_request_destructor ( void * ptr )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_request * req = talloc_get_type ( ptr , struct ldap_request ) ;
if ( req - > state = = LDAP_REQUEST_PENDING ) {
DLIST_REMOVE ( req - > conn - > pending , req ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
return 0 ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
called on timeout of a ldap request
*/
static void ldap_request_timeout ( struct event_context * ev , struct timed_event * te ,
2006-02-22 04:31:35 +03:00
struct timeval t , void * private_data )
2004-11-06 23:15:39 +03:00
{
2006-02-22 04:31:35 +03:00
struct ldap_request * req = talloc_get_type ( private_data , struct ldap_request ) ;
2005-06-16 09:39:40 +04:00
req - > status = NT_STATUS_IO_TIMEOUT ;
if ( req - > state = = LDAP_REQUEST_PENDING ) {
DLIST_REMOVE ( req - > conn - > pending , req ) ;
}
req - > state = LDAP_REQUEST_DONE ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
2004-11-06 23:15:39 +03:00
}
}
2005-11-10 03:28:02 +03:00
/*
called on completion of a one - way ldap request
*/
static void ldap_request_complete ( struct event_context * ev , struct timed_event * te ,
2006-02-22 04:31:35 +03:00
struct timeval t , void * private_data )
2005-11-10 03:28:02 +03:00
{
2006-02-22 04:31:35 +03:00
struct ldap_request * req = talloc_get_type ( private_data , struct ldap_request ) ;
2005-11-10 03:28:02 +03:00
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
}
2005-06-16 09:39:40 +04:00
/*
send a ldap message - async interface
*/
struct ldap_request * ldap_request_send ( struct ldap_connection * conn ,
struct ldap_message * msg )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_request * req ;
2006-04-25 15:50:32 +04:00
NTSTATUS status = NT_STATUS_UNSUCCESSFUL ;
req = talloc_zero ( conn , struct ldap_request ) ;
if ( req = = NULL ) return NULL ;
2004-11-06 23:15:39 +03:00
2005-06-20 05:17:29 +04:00
if ( conn - > tls = = NULL ) {
2006-04-25 15:50:32 +04:00
status = NT_STATUS_INVALID_CONNECTION ;
goto failed ;
2005-06-16 09:39:40 +04:00
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
req - > state = LDAP_REQUEST_SEND ;
req - > conn = conn ;
req - > messageid = conn - > next_messageid + + ;
2005-07-17 14:52:31 +04:00
if ( conn - > next_messageid = = 0 ) {
conn - > next_messageid = 1 ;
}
2005-06-16 09:39:40 +04:00
req - > type = msg - > type ;
if ( req - > messageid = = - 1 ) {
goto failed ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
talloc_set_destructor ( req , ldap_request_destructor ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
msg - > messageid = req - > messageid ;
2004-11-06 23:15:39 +03:00
2005-09-14 02:05:45 +04:00
if ( ! ldap_encode ( msg , & req - > data , req ) ) {
2005-06-16 09:39:40 +04:00
goto failed ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* possibly encrypt/sign the request */
if ( conn - > enable_wrap ) {
DATA_BLOB wrapped ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
status = gensec_wrap ( conn - > gensec , req , & req - > data , & wrapped ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto failed ;
}
data_blob_free ( & req - > data ) ;
req - > data = data_blob_talloc ( req , NULL , wrapped . length + 4 ) ;
if ( req - > data . data = = NULL ) {
goto failed ;
}
RSIVAL ( req - > data . data , 0 , wrapped . length ) ;
memcpy ( req - > data . data + 4 , wrapped . data , wrapped . length ) ;
data_blob_free ( & wrapped ) ;
2004-11-06 23:15:39 +03:00
}
2005-11-10 03:28:02 +03:00
status = packet_send ( conn - > packet , req - > data ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto failed ;
}
2005-06-16 09:39:40 +04:00
2005-11-10 03:28:02 +03:00
/* some requests don't expect a reply, so don't add those to the
pending queue */
if ( req - > type = = LDAP_TAG_AbandonRequest | |
req - > type = = LDAP_TAG_UnbindRequest ) {
req - > status = NT_STATUS_OK ;
req - > state = LDAP_REQUEST_DONE ;
/* we can't call the async callback now, as it isn't setup, so
call it as next event */
event_add_timed ( conn - > event . event_ctx , req , timeval_zero ( ) ,
ldap_request_complete , req ) ;
return req ;
2004-11-06 23:15:39 +03:00
}
2005-11-10 03:28:02 +03:00
req - > state = LDAP_REQUEST_PENDING ;
DLIST_ADD ( conn - > pending , req ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* put a timeout on the request */
2006-02-22 04:31:35 +03:00
req - > time_event = event_add_timed ( conn - > event . event_ctx , req ,
timeval_current_ofs ( conn - > timeout , 0 ) ,
ldap_request_timeout , req ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
return req ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
failed :
2006-04-25 15:50:32 +04:00
req - > status = status ;
req - > state = LDAP_REQUEST_ERROR ;
event_add_timed ( conn - > event . event_ctx , req , timeval_zero ( ) ,
ldap_request_complete , req ) ;
return req ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
wait for a request to complete
note that this does not destroy the request
*/
NTSTATUS ldap_request_wait ( struct ldap_request * req )
{
2006-04-26 20:52:45 +04:00
while ( req - > state < LDAP_REQUEST_DONE ) {
2005-06-16 09:39:40 +04:00
if ( event_loop_once ( req - > conn - > event . event_ctx ) ! = 0 ) {
req - > status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
break ;
}
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
return req - > status ;
2004-11-06 23:15:39 +03:00
}
2005-10-17 15:50:34 +04:00
/*
a mapping of ldap response code to strings
*/
static const struct {
enum ldap_result_code code ;
const char * str ;
} ldap_code_map [ ] = {
# define _LDAP_MAP_CODE(c) { c, #c }
_LDAP_MAP_CODE ( LDAP_SUCCESS ) ,
_LDAP_MAP_CODE ( LDAP_OPERATIONS_ERROR ) ,
_LDAP_MAP_CODE ( LDAP_PROTOCOL_ERROR ) ,
_LDAP_MAP_CODE ( LDAP_TIME_LIMIT_EXCEEDED ) ,
_LDAP_MAP_CODE ( LDAP_SIZE_LIMIT_EXCEEDED ) ,
_LDAP_MAP_CODE ( LDAP_COMPARE_FALSE ) ,
_LDAP_MAP_CODE ( LDAP_COMPARE_TRUE ) ,
_LDAP_MAP_CODE ( LDAP_AUTH_METHOD_NOT_SUPPORTED ) ,
_LDAP_MAP_CODE ( LDAP_STRONG_AUTH_REQUIRED ) ,
_LDAP_MAP_CODE ( LDAP_REFERRAL ) ,
_LDAP_MAP_CODE ( LDAP_ADMIN_LIMIT_EXCEEDED ) ,
_LDAP_MAP_CODE ( LDAP_UNAVAILABLE_CRITICAL_EXTENSION ) ,
_LDAP_MAP_CODE ( LDAP_CONFIDENTIALITY_REQUIRED ) ,
_LDAP_MAP_CODE ( LDAP_SASL_BIND_IN_PROGRESS ) ,
_LDAP_MAP_CODE ( LDAP_NO_SUCH_ATTRIBUTE ) ,
_LDAP_MAP_CODE ( LDAP_UNDEFINED_ATTRIBUTE_TYPE ) ,
_LDAP_MAP_CODE ( LDAP_INAPPROPRIATE_MATCHING ) ,
_LDAP_MAP_CODE ( LDAP_CONSTRAINT_VIOLATION ) ,
_LDAP_MAP_CODE ( LDAP_ATTRIBUTE_OR_VALUE_EXISTS ) ,
_LDAP_MAP_CODE ( LDAP_INVALID_ATTRIBUTE_SYNTAX ) ,
_LDAP_MAP_CODE ( LDAP_NO_SUCH_OBJECT ) ,
_LDAP_MAP_CODE ( LDAP_ALIAS_PROBLEM ) ,
_LDAP_MAP_CODE ( LDAP_INVALID_DN_SYNTAX ) ,
_LDAP_MAP_CODE ( LDAP_ALIAS_DEREFERENCING_PROBLEM ) ,
_LDAP_MAP_CODE ( LDAP_INAPPROPRIATE_AUTHENTICATION ) ,
_LDAP_MAP_CODE ( LDAP_INVALID_CREDENTIALS ) ,
_LDAP_MAP_CODE ( LDAP_INSUFFICIENT_ACCESS_RIGHTs ) ,
_LDAP_MAP_CODE ( LDAP_BUSY ) ,
_LDAP_MAP_CODE ( LDAP_UNAVAILABLE ) ,
_LDAP_MAP_CODE ( LDAP_UNWILLING_TO_PERFORM ) ,
_LDAP_MAP_CODE ( LDAP_LOOP_DETECT ) ,
_LDAP_MAP_CODE ( LDAP_NAMING_VIOLATION ) ,
_LDAP_MAP_CODE ( LDAP_OBJECT_CLASS_VIOLATION ) ,
_LDAP_MAP_CODE ( LDAP_NOT_ALLOWED_ON_NON_LEAF ) ,
_LDAP_MAP_CODE ( LDAP_NOT_ALLOWED_ON_RDN ) ,
_LDAP_MAP_CODE ( LDAP_ENTRY_ALREADY_EXISTS ) ,
_LDAP_MAP_CODE ( LDAP_OBJECT_CLASS_MODS_PROHIBITED ) ,
_LDAP_MAP_CODE ( LDAP_AFFECTS_MULTIPLE_DSAS ) ,
_LDAP_MAP_CODE ( LDAP_OTHER )
} ;
2005-06-16 09:39:40 +04:00
/*
used to setup the status code from a ldap response
*/
NTSTATUS ldap_check_response ( struct ldap_connection * conn , struct ldap_Result * r )
{
2005-10-17 15:50:34 +04:00
int i ;
const char * codename = " unknown " ;
2005-06-16 09:39:40 +04:00
if ( r - > resultcode = = LDAP_SUCCESS ) {
return NT_STATUS_OK ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( conn - > last_error ) {
talloc_free ( conn - > last_error ) ;
}
2005-10-17 15:50:34 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( ldap_code_map ) ; i + + ) {
if ( r - > resultcode = = ldap_code_map [ i ] . code ) {
codename = ldap_code_map [ i ] . str ;
break ;
}
}
conn - > last_error = talloc_asprintf ( conn , " LDAP error %u %s - %s <%s> <%s> " ,
2005-06-16 09:39:40 +04:00
r - > resultcode ,
2005-10-17 15:50:34 +04:00
codename ,
2005-06-18 13:01:51 +04:00
r - > dn ? r - > dn : " (NULL) " ,
r - > errormessage ? r - > errormessage : " " ,
r - > referral ? r - > referral : " " ) ;
2005-06-16 09:39:40 +04:00
return NT_STATUS_LDAP ( r - > resultcode ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
return error string representing the last error
*/
const char * ldap_errstr ( struct ldap_connection * conn , NTSTATUS status )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
if ( NT_STATUS_IS_LDAP ( status ) & & conn - > last_error ! = NULL ) {
return conn - > last_error ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
return nt_errstr ( status ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
return the Nth result message , waiting if necessary
*/
NTSTATUS ldap_result_n ( struct ldap_request * req , int n , struct ldap_message * * msg )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
* msg = NULL ;
2004-11-06 23:15:39 +03:00
2005-06-17 06:45:40 +04:00
NT_STATUS_HAVE_NO_MEMORY ( req ) ;
2006-04-26 20:52:45 +04:00
while ( req - > state < LDAP_REQUEST_DONE & & n > = req - > num_replies ) {
2005-06-16 09:39:40 +04:00
if ( event_loop_once ( req - > conn - > event . event_ctx ) ! = 0 ) {
return NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
}
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( n < req - > num_replies ) {
* msg = req - > replies [ n ] ;
return NT_STATUS_OK ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_IS_OK ( req - > status ) ) {
return req - > status ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
return NT_STATUS_NO_MORE_ENTRIES ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
return a single result message , checking if it is of the expected LDAP type
*/
NTSTATUS ldap_result_one ( struct ldap_request * req , struct ldap_message * * msg , int type )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
NTSTATUS status ;
status = ldap_result_n ( req , 0 , msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( ( * msg ) - > type ! = type ) {
* msg = NULL ;
return NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
}
return status ;
2004-11-06 23:15:39 +03:00
}
2005-06-17 06:45:40 +04:00
/*
a simple ldap transaction , for single result requests that only need a status code
this relies on single valued requests having the response type = = request type + 1
*/
NTSTATUS ldap_transaction ( struct ldap_connection * conn , struct ldap_message * msg )
{
struct ldap_request * req = ldap_request_send ( conn , msg ) ;
struct ldap_message * res ;
NTSTATUS status ;
status = ldap_result_n ( req , 0 , & res ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( req ) ;
return status ;
}
if ( res - > type ! = msg - > type + 1 ) {
talloc_free ( req ) ;
return NT_STATUS_LDAP ( LDAP_PROTOCOL_ERROR ) ;
}
status = ldap_check_response ( conn , & res - > r . GeneralResult ) ;
talloc_free ( req ) ;
return status ;
}