2004-09-13 14:36:59 +04:00
/*
Unix SMB / CIFS implementation .
2005-06-19 11:21:18 +04:00
2004-09-13 14:36:59 +04:00
LDAP server
2005-06-19 11:21:18 +04:00
Copyright ( C ) Andrew Tridgell 2005
2004-09-13 14:36:59 +04:00
Copyright ( C ) Volker Lendecke 2004
Copyright ( C ) Stefan Metzmacher 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"
2005-02-03 14:56:03 +03:00
# include "lib/events/events.h"
2004-11-02 05:57:18 +03:00
# include "auth/auth.h"
2004-11-02 09:42:15 +03:00
# include "dlinklist.h"
# include "asn_1.h"
2004-11-02 09:52:59 +03:00
# include "ldap_server/ldap_server.h"
2005-06-19 11:21:18 +04:00
# include "smbd/service_task.h"
2005-01-30 03:54:57 +03:00
# include "smbd/service_stream.h"
2005-02-10 09:59:29 +03:00
# include "lib/socket/socket.h"
2005-06-19 11:21:18 +04:00
# include "lib/tls/tls.h"
2004-09-13 14:36:59 +04:00
/*
close the socket and shutdown a server_context
*/
2005-06-19 13:31:34 +04:00
static void ldapsrv_terminate_connection ( struct ldapsrv_connection * conn ,
const char * reason )
2004-09-13 14:36:59 +04:00
{
2005-06-19 13:31:34 +04:00
talloc_free ( conn - > tls ) ;
conn - > tls = NULL ;
stream_terminate_connection ( conn - > connection , reason ) ;
2004-09-13 14:36:59 +04:00
}
2005-06-19 13:31:34 +04:00
/*
process a decoded ldap message
*/
static void ldapsrv_process_message ( struct ldapsrv_connection * conn ,
struct ldap_message * msg )
2004-09-13 14:36:59 +04:00
{
2005-06-19 13:31:34 +04:00
struct ldapsrv_call * call ;
2004-09-20 16:31:07 +04:00
NTSTATUS status ;
2005-06-19 13:31:34 +04:00
DATA_BLOB blob ;
struct ldapsrv_send * q ;
BOOL enable_wrap = conn - > enable_wrap ;
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
call = talloc ( conn , struct ldapsrv_call ) ;
if ( ! call ) {
ldapsrv_terminate_connection ( conn , " no memory " ) ;
return ;
2004-10-28 08:00:43 +04:00
}
2005-06-19 13:31:34 +04:00
call - > request = talloc_steal ( call , msg ) ;
call - > conn = conn ;
call - > replies = NULL ;
2004-10-28 08:00:43 +04:00
2005-06-19 13:31:34 +04:00
/* make the call */
status = ldapsrv_do_call ( call ) ;
2005-06-19 11:21:18 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-06-19 13:31:34 +04:00
goto failed ;
2004-10-08 16:19:08 +04:00
}
2005-06-19 13:31:34 +04:00
blob = data_blob ( NULL , 0 ) ;
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
if ( call - > replies = = NULL ) {
talloc_free ( call ) ;
return ;
2004-10-28 08:00:43 +04:00
}
2005-06-19 13:31:34 +04:00
/* build all the replies into a single blob */
while ( call - > replies ) {
DATA_BLOB b ;
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
msg = call - > replies - > msg ;
if ( ! ldap_encode ( msg , & b ) ) {
DEBUG ( 0 , ( " Failed to encode ldap reply of type %d \n " , msg - > type ) ) ;
goto failed ;
}
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
status = data_blob_append ( call , & blob , b . data , b . length ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
DLIST_REMOVE ( call - > replies , call - > replies ) ;
2004-10-08 16:19:08 +04:00
}
2005-06-19 13:31:34 +04:00
/* possibly encrypt/sign the reply */
if ( enable_wrap ) {
DATA_BLOB wrapped ;
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
status = gensec_wrap ( conn - > gensec , call , & blob , & wrapped ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto failed ;
}
data_blob_free ( & blob ) ;
blob = data_blob_talloc ( call , NULL , wrapped . length + 4 ) ;
if ( q - > data . data = = NULL ) {
goto failed ;
}
RSIVAL ( blob . data , 0 , wrapped . length ) ;
memcpy ( blob . data + 4 , wrapped . data , wrapped . length ) ;
data_blob_free ( & wrapped ) ;
2004-10-08 16:19:08 +04:00
}
2005-06-19 13:31:34 +04:00
q = talloc ( conn , struct ldapsrv_send ) ;
if ( q = = NULL ) goto failed ;
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
q - > data = blob ;
talloc_steal ( q , blob . data ) ;
if ( conn - > send_queue = = NULL ) {
EVENT_FD_WRITEABLE ( conn - > connection - > event . fde ) ;
2004-10-08 16:19:08 +04:00
}
2005-06-19 13:31:34 +04:00
DLIST_ADD_END ( conn - > send_queue , q , struct ldapsrv_send * ) ;
talloc_free ( call ) ;
return ;
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
failed :
talloc_free ( call ) ;
2004-10-08 16:19:08 +04:00
}
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
/*
try and decode the partial input buffer
*/
static void ldapsrv_try_decode_plain ( struct ldapsrv_connection * conn )
2004-09-13 14:36:59 +04:00
{
2005-06-19 13:31:34 +04:00
struct asn1_data asn1 ;
2004-10-10 02:00:00 +04:00
2005-06-19 13:31:34 +04:00
file_save ( " asn1.dat " , conn - > partial . data , conn - > partial . length ) ;
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
if ( ! asn1_load ( & asn1 , conn - > partial ) ) {
ldapsrv_terminate_connection ( conn , " out of memory " ) ;
return ;
2004-10-08 16:19:08 +04:00
}
2005-06-19 13:31:34 +04:00
/* try and decode - this will fail if we don't have a full packet yet */
while ( asn1 . ofs < asn1 . length ) {
struct ldap_message * msg = talloc ( conn , struct ldap_message ) ;
off_t saved_ofs = asn1 . ofs ;
if ( msg = = NULL ) {
ldapsrv_terminate_connection ( conn , " out of memory " ) ;
return ;
}
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
if ( ldap_decode ( & asn1 , msg ) ) {
ldapsrv_process_message ( conn , msg ) ;
} else {
if ( asn1 . ofs < asn1 . length ) {
ldapsrv_terminate_connection ( conn , " ldap_decode failed " ) ;
return ;
}
asn1 . ofs = saved_ofs ;
talloc_free ( msg ) ;
break ;
}
2004-10-08 16:19:08 +04:00
}
2005-06-19 13:31:34 +04:00
/* keep any remaining data in conn->partial */
data_blob_free ( & conn - > partial ) ;
if ( asn1 . ofs ! = asn1 . length ) {
conn - > partial = data_blob_talloc ( conn ,
asn1 . data + asn1 . ofs ,
asn1 . length - asn1 . ofs ) ;
2004-10-08 16:19:08 +04:00
}
2005-06-19 13:31:34 +04:00
asn1_free ( & asn1 ) ;
}
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
/*
try and decode / process wrapped data
*/
static void ldapsrv_try_decode_wrapped ( struct ldapsrv_connection * conn )
{
uint32_t len ;
2004-10-08 16:19:08 +04:00
2005-06-19 13:31:34 +04:00
/* keep decoding while we have a full wrapped packet */
while ( conn - > partial . length > = 4 & &
( len = RIVAL ( conn - > partial . data , 0 ) ) < = conn - > partial . length - 4 ) {
DATA_BLOB wrapped , unwrapped ;
struct asn1_data asn1 ;
struct ldap_message * msg = talloc ( conn , struct ldap_message ) ;
NTSTATUS status ;
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
if ( msg = = NULL ) {
ldapsrv_terminate_connection ( conn , " out of memory " ) ;
return ;
}
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
wrapped . data = conn - > partial . data + 4 ;
wrapped . length = len ;
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
status = gensec_unwrap ( conn - > gensec , msg , & wrapped , & unwrapped ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldapsrv_terminate_connection ( conn , " gensec unwrap failed " ) ;
return ;
}
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
if ( ! asn1_load ( & asn1 , unwrapped ) ) {
ldapsrv_terminate_connection ( conn , " out of memory " ) ;
return ;
}
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
while ( ldap_decode ( & asn1 , msg ) ) {
ldapsrv_process_message ( conn , msg ) ;
msg = talloc ( conn , struct ldap_message ) ;
}
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
if ( asn1 . ofs < asn1 . length ) {
ldapsrv_terminate_connection ( conn , " ldap_decode failed " ) ;
return ;
}
talloc_free ( msg ) ;
asn1_free ( & asn1 ) ;
if ( conn - > partial . length = = len + 4 ) {
data_blob_free ( & conn - > partial ) ;
} else {
memmove ( conn - > partial . data , conn - > partial . data + len + 4 ,
conn - > partial . length - ( len + 4 ) ) ;
conn - > partial . length - = len + 4 ;
2004-09-22 14:48:32 +04:00
}
2004-09-13 14:36:59 +04:00
}
}
2004-10-10 02:00:00 +04:00
2004-09-13 14:36:59 +04:00
/*
called when a LDAP socket becomes readable
*/
2005-06-19 13:31:34 +04:00
static void ldapsrv_recv ( struct stream_connection * c , uint16_t flags )
2004-09-13 14:36:59 +04:00
{
2005-06-19 13:31:34 +04:00
struct ldapsrv_connection * conn =
talloc_get_type ( c - > private , struct ldapsrv_connection ) ;
2004-09-27 17:20:59 +04:00
NTSTATUS status ;
2005-06-19 13:31:34 +04:00
size_t npending , nread ;
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
/* work out how much data is pending */
status = tls_socket_pending ( conn - > tls , & npending ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldapsrv_terminate_connection ( conn , " socket_pening() failed " ) ;
return ;
}
if ( npending = = 0 ) {
2005-06-19 14:37:45 +04:00
ldapsrv_terminate_connection ( conn , " EOF from client " ) ;
2004-09-13 14:36:59 +04:00
return ;
}
2005-06-19 13:31:34 +04:00
conn - > partial . data = talloc_realloc_size ( conn , conn - > partial . data ,
conn - > partial . length + npending ) ;
if ( conn - > partial . data = = NULL ) {
ldapsrv_terminate_connection ( conn , " out of memory " ) ;
return ;
2004-09-13 14:36:59 +04:00
}
2005-06-19 13:31:34 +04:00
/* receive the data */
status = tls_socket_recv ( conn - > tls , conn - > partial . data + conn - > partial . length ,
npending , & nread ) ;
if ( NT_STATUS_IS_ERR ( status ) ) {
ldapsrv_terminate_connection ( conn , " socket_recv() failed " ) ;
return ;
}
2004-09-27 17:20:59 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return ;
}
2005-06-19 13:31:34 +04:00
if ( nread = = 0 ) {
ldapsrv_terminate_connection ( conn , " EOF from client " ) ;
return ;
2004-09-22 14:48:32 +04:00
}
2005-06-19 13:31:34 +04:00
conn - > partial . length + = nread ;
2004-09-18 12:13:06 +04:00
2005-06-19 13:31:34 +04:00
/* see if we can decode what we have */
if ( conn - > enable_wrap ) {
ldapsrv_try_decode_wrapped ( conn ) ;
} else {
ldapsrv_try_decode_plain ( conn ) ;
}
2004-09-13 14:36:59 +04:00
}
/*
called when a LDAP socket becomes writable
*/
2005-06-19 13:31:34 +04:00
static void ldapsrv_send ( struct stream_connection * c , uint16_t flags )
2004-09-13 14:36:59 +04:00
{
2005-06-19 13:31:34 +04:00
struct ldapsrv_connection * conn =
talloc_get_type ( c - > private , struct ldapsrv_connection ) ;
while ( conn - > send_queue ) {
struct ldapsrv_send * q = conn - > send_queue ;
size_t nsent ;
NTSTATUS status ;
status = tls_socket_send ( conn - > tls , & q - > data , & nsent ) ;
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
break ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldapsrv_terminate_connection ( conn , " socket_send error " ) ;
return ;
}
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
q - > data . data + = nsent ;
q - > data . length - = nsent ;
if ( q - > data . length = = 0 ) {
DLIST_REMOVE ( conn - > send_queue , q ) ;
}
2004-09-13 14:36:59 +04:00
}
2005-06-19 13:31:34 +04:00
if ( conn - > send_queue = = NULL ) {
EVENT_FD_NOT_WRITEABLE ( c - > event . fde ) ;
2004-09-22 14:48:32 +04:00
}
2004-09-13 14:36:59 +04:00
}
/*
initialise a server_context from a open socket and register a event handler
for reading from that socket
*/
2005-06-19 13:31:34 +04:00
static void ldapsrv_accept ( struct stream_connection * c )
2004-09-13 14:36:59 +04:00
{
2005-06-19 11:21:18 +04:00
struct ldapsrv_service * ldapsrv_service =
2005-06-19 13:31:34 +04:00
talloc_get_type ( c - > private , struct ldapsrv_service ) ;
struct ldapsrv_connection * conn ;
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
conn = talloc_zero ( c , struct ldapsrv_connection ) ;
if ( conn = = NULL ) goto failed ;
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
conn - > enable_wrap = False ;
conn - > partial = data_blob ( NULL , 0 ) ;
conn - > send_queue = NULL ;
conn - > connection = c ;
conn - > service = talloc_get_type ( c - > private , struct ldapsrv_service ) ;
c - > private = conn ;
2005-06-19 11:21:18 +04:00
/* note that '0' is a ASN1_SEQUENCE(0), which is the first byte on
any ldap connection */
2005-06-19 13:31:34 +04:00
conn - > tls = tls_init_server ( ldapsrv_service - > tls_params , c - > socket ,
c - > event . fde , " 0 " ) ;
if ( conn - > tls = = NULL ) goto failed ;
2005-06-19 11:21:18 +04:00
return ;
failed :
2005-06-19 13:31:34 +04:00
talloc_free ( c ) ;
2004-09-13 14:36:59 +04:00
}
2005-01-30 03:54:57 +03:00
static const struct stream_server_ops ldap_stream_ops = {
2005-01-14 04:32:56 +03:00
. name = " ldap " ,
. accept_connection = ldapsrv_accept ,
. recv_handler = ldapsrv_recv ,
. send_handler = ldapsrv_send ,
} ;
2005-01-30 03:54:57 +03:00
/*
add a socket address to the list of events , one event per port
*/
2005-06-19 13:31:34 +04:00
static NTSTATUS add_socket ( struct event_context * event_context ,
const struct model_ops * model_ops ,
2005-01-30 03:54:57 +03:00
const char * address , struct ldapsrv_service * ldap_service )
2004-09-13 14:36:59 +04:00
{
2005-01-30 03:54:57 +03:00
uint16_t port = 389 ;
NTSTATUS status ;
status = stream_setup_socket ( event_context , model_ops , & ldap_stream_ops ,
" ipv4 " , address , & port , ldap_service ) ;
2005-06-19 11:21:18 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " ldapsrv failed to bind to %s:%u - %s \n " ,
address , port , nt_errstr ( status ) ) ) ;
}
2005-01-30 03:54:57 +03:00
2005-06-19 13:31:34 +04:00
if ( tls_support ( ldap_service - > tls_params ) ) {
/* add ldaps server */
port = 636 ;
status = stream_setup_socket ( event_context , model_ops , & ldap_stream_ops ,
" ipv4 " , address , & port , ldap_service ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " ldapsrv failed to bind to %s:%u - %s \n " ,
address , port , nt_errstr ( status ) ) ) ;
}
2005-06-19 11:21:18 +04:00
}
2005-06-19 13:31:34 +04:00
2005-06-19 11:21:18 +04:00
return status ;
2004-09-13 14:36:59 +04:00
}
2005-01-30 03:54:57 +03:00
/*
open the ldap server sockets
*/
2005-06-19 11:21:18 +04:00
static void ldapsrv_task_init ( struct task_server * task )
2005-01-30 03:54:57 +03:00
{
struct ldapsrv_service * ldap_service ;
struct ldapsrv_partition * rootDSE_part ;
struct ldapsrv_partition * part ;
NTSTATUS status ;
2004-09-13 14:36:59 +04:00
2005-06-19 11:21:18 +04:00
ldap_service = talloc_zero ( task , struct ldapsrv_service ) ;
if ( ldap_service = = NULL ) goto failed ;
2005-01-30 03:54:57 +03:00
2005-06-19 11:21:18 +04:00
ldap_service - > tls_params = tls_initialise ( ldap_service ) ;
if ( ldap_service - > tls_params = = NULL ) goto failed ;
2005-01-30 03:54:57 +03:00
rootDSE_part = talloc ( ldap_service , struct ldapsrv_partition ) ;
2005-06-19 11:21:18 +04:00
if ( rootDSE_part = = NULL ) goto failed ;
2005-01-30 03:54:57 +03:00
rootDSE_part - > base_dn = " " ; /* RootDSE */
rootDSE_part - > ops = ldapsrv_get_rootdse_partition_ops ( ) ;
ldap_service - > rootDSE = rootDSE_part ;
DLIST_ADD_END ( ldap_service - > partitions , rootDSE_part , struct ldapsrv_partition * ) ;
part = talloc ( ldap_service , struct ldapsrv_partition ) ;
2005-06-19 11:21:18 +04:00
if ( part = = NULL ) goto failed ;
2005-01-30 03:54:57 +03:00
part - > base_dn = " * " ; /* default partition */
if ( lp_parm_bool ( - 1 , " ldapsrv " , " hacked " , False ) ) {
part - > ops = ldapsrv_get_hldb_partition_ops ( ) ;
} else {
part - > ops = ldapsrv_get_sldb_partition_ops ( ) ;
}
ldap_service - > default_partition = part ;
DLIST_ADD_END ( ldap_service - > partitions , part , struct ldapsrv_partition * ) ;
if ( lp_interfaces ( ) & & lp_bind_interfaces_only ( ) ) {
int num_interfaces = iface_count ( ) ;
int i ;
/* We have been given an interfaces line, and been
told to only bind to those interfaces . Create a
socket per interface and bind to only these .
*/
for ( i = 0 ; i < num_interfaces ; i + + ) {
2005-02-10 06:22:47 +03:00
const char * address = iface_n_ip ( i ) ;
2005-06-19 11:21:18 +04:00
status = add_socket ( task - > event_ctx , task - > model_ops , address , ldap_service ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
2005-01-30 03:54:57 +03:00
}
} else {
2005-06-19 11:21:18 +04:00
status = add_socket ( task - > event_ctx , task - > model_ops , lp_socket_address ( ) , ldap_service ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
2005-01-30 03:54:57 +03:00
}
2005-06-19 11:21:18 +04:00
return ;
failed :
task_terminate ( task , " Failed to startup ldap server task " ) ;
}
/*
called on startup of the web server service It ' s job is to start
listening on all configured sockets
*/
static NTSTATUS ldapsrv_init ( struct event_context * event_context ,
const struct model_ops * model_ops )
{
return task_server_startup ( event_context , model_ops , ldapsrv_task_init ) ;
2004-09-13 14:36:59 +04:00
}
2005-01-30 03:54:57 +03:00
2004-09-13 14:36:59 +04:00
NTSTATUS server_service_ldap_init ( void )
{
2005-01-30 03:54:57 +03:00
return register_server_service ( " ldap " , ldapsrv_init ) ;
2004-09-13 14:36:59 +04:00
}