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
2009-05-29 11:42:31 +04:00
2004-09-13 14:36:59 +04:00
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-09-13 14:36:59 +04:00
( at your option ) any later version .
2009-05-29 11:42:31 +04:00
2004-09-13 14:36:59 +04:00
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 .
2009-05-29 11:42:31 +04:00
2004-09-13 14:36:59 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-09-13 14:36:59 +04:00
*/
# include "includes.h"
2010-09-22 16:24:03 +04:00
# include "system/network.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"
2006-11-07 03:48:36 +03:00
# include "auth/credentials/credentials.h"
# include "librpc/gen_ndr/ndr_samr.h"
2008-10-11 23:31:42 +04:00
# include "../lib/util/dlinklist.h"
# include "../lib/util/asn1.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"
2006-03-07 17:13:38 +03:00
# include "smbd/service.h"
2006-08-21 05:25:20 +04:00
# include "smbd/process_model.h"
2005-06-19 11:21:18 +04:00
# include "lib/tls/tls.h"
2005-07-10 05:08:10 +04:00
# include "lib/messaging/irpc.h"
2006-01-13 18:40:15 +03:00
# include "lib/ldb/include/ldb.h"
# include "lib/ldb/include/ldb_errors.h"
2008-04-02 06:53:27 +04:00
# include "libcli/ldap/ldap_proto.h"
2006-03-07 14:07:23 +03:00
# include "system/network.h"
2006-08-17 17:37:04 +04:00
# include "lib/socket/netif.h"
2006-12-13 14:19:51 +03:00
# include "dsdb/samdb/samdb.h"
2007-09-08 16:42:09 +04:00
# include "param/param.h"
2010-09-22 16:24:03 +04:00
# include "../lib/tsocket/tsocket.h"
# include "../lib/util/tevent_ntstatus.h"
# include "../libcli/util/tstream.h"
2010-10-05 09:47:51 +04:00
2010-09-22 16:24:03 +04:00
static void ldapsrv_terminate_connection_done ( struct tevent_req * subreq ) ;
2004-10-08 16:19:08 +04:00
2010-10-05 09:47:51 +04:00
/*
2010-09-22 16:24:03 +04:00
close the socket and shutdown a server_context
2010-10-05 09:47:51 +04:00
*/
2010-09-22 16:24:03 +04:00
static void ldapsrv_terminate_connection ( struct ldapsrv_connection * conn ,
const char * reason )
2010-10-05 09:47:51 +04:00
{
2010-09-22 16:24:03 +04:00
struct tevent_req * subreq ;
2010-10-05 09:47:51 +04:00
2010-09-22 16:24:03 +04:00
if ( conn - > limits . reason ) {
2010-09-22 16:24:03 +04:00
return ;
2010-09-07 05:57:44 +04:00
}
2010-09-22 16:24:03 +04:00
conn - > limits . endtime = timeval_current_ofs ( 0 , 500 ) ;
2010-10-05 09:47:51 +04:00
2010-09-22 16:24:03 +04:00
DEBUG ( 2 , ( " ldapsrv_terminate_connection: %s - disconnecting \n " ,
reason ) ) ;
2010-10-05 09:47:51 +04:00
2010-09-22 16:24:03 +04:00
tevent_queue_stop ( conn - > sockets . send_queue ) ;
if ( conn - > active_call ) {
tevent_req_cancel ( conn - > active_call ) ;
conn - > active_call = NULL ;
2010-10-05 09:47:51 +04:00
}
2010-09-22 16:24:03 +04:00
conn - > limits . reason = talloc_strdup ( conn , reason ) ;
if ( conn - > limits . reason = = NULL ) {
TALLOC_FREE ( conn - > sockets . tls ) ;
TALLOC_FREE ( conn - > sockets . sasl ) ;
TALLOC_FREE ( conn - > sockets . raw ) ;
stream_terminate_connection ( conn - > connection , reason ) ;
return ;
2010-09-07 05:57:44 +04:00
}
2010-09-22 16:24:03 +04:00
subreq = tstream_disconnect_send ( conn ,
conn - > connection - > event . ctx ,
conn - > sockets . active ) ;
if ( subreq = = NULL ) {
TALLOC_FREE ( conn - > sockets . tls ) ;
TALLOC_FREE ( conn - > sockets . sasl ) ;
TALLOC_FREE ( conn - > sockets . raw ) ;
stream_terminate_connection ( conn - > connection , reason ) ;
return ;
2010-10-05 09:47:51 +04:00
}
2010-09-22 16:24:03 +04:00
tevent_req_set_endtime ( subreq ,
conn - > connection - > event . ctx ,
conn - > limits . endtime ) ;
tevent_req_set_callback ( subreq , ldapsrv_terminate_connection_done , conn ) ;
2010-10-05 09:47:51 +04:00
}
2004-10-10 02:00:00 +04:00
2010-09-22 16:24:03 +04:00
static void ldapsrv_terminate_connection_done ( struct tevent_req * subreq )
2010-10-05 09:47:51 +04:00
{
2010-09-22 16:24:03 +04:00
struct ldapsrv_connection * conn =
tevent_req_callback_data ( subreq ,
struct ldapsrv_connection ) ;
int ret ;
int sys_errno ;
2004-10-08 16:19:08 +04:00
2010-09-22 16:24:03 +04:00
ret = tstream_disconnect_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
2004-10-08 16:19:08 +04:00
2010-09-22 16:24:03 +04:00
if ( conn - > sockets . active = = conn - > sockets . raw ) {
TALLOC_FREE ( conn - > sockets . tls ) ;
TALLOC_FREE ( conn - > sockets . sasl ) ;
TALLOC_FREE ( conn - > sockets . raw ) ;
stream_terminate_connection ( conn - > connection ,
conn - > limits . reason ) ;
return ;
2004-10-08 16:19:08 +04:00
}
2010-10-05 09:47:51 +04:00
2010-09-22 16:24:03 +04:00
TALLOC_FREE ( conn - > sockets . tls ) ;
TALLOC_FREE ( conn - > sockets . sasl ) ;
conn - > sockets . active = conn - > sockets . raw ;
subreq = tstream_disconnect_send ( conn ,
conn - > connection - > event . ctx ,
conn - > sockets . active ) ;
if ( subreq = = NULL ) {
TALLOC_FREE ( conn - > sockets . raw ) ;
stream_terminate_connection ( conn - > connection ,
conn - > limits . reason ) ;
return ;
2010-10-05 09:47:51 +04:00
}
2010-09-22 16:24:03 +04:00
tevent_req_set_endtime ( subreq ,
conn - > connection - > event . ctx ,
conn - > limits . endtime ) ;
tevent_req_set_callback ( subreq , ldapsrv_terminate_connection_done , conn ) ;
2006-01-14 01:48:08 +03:00
}
2004-09-13 14:36:59 +04:00
/*
called when a LDAP socket becomes readable
*/
r17197: This patch moves the encryption of bulk data on SASL negotiated security
contexts from the application layer into the socket layer.
This improves a number of correctness aspects, as we now allow LDAP
packets to cross multiple SASL packets. It should also make it much
easier to write async LDAP tests from windows clients, as they use SASL
by default. It is also vital to allowing OpenLDAP clients to use GSSAPI
against Samba4, as it negotiates a rather small SASL buffer size.
This patch mirrors the earlier work done to move TLS into the socket
layer.
Unusual in this pstch is the extra read callback argument I take. As
SASL is a layer on top of a socket, it is entirely possible for the
SASL layer to drain a socket dry, but for the caller not to have read
all the decrypted data. This would leave the system without an event
to restart the read (as the socket is dry).
As such, I re-invoke the read handler from a timed callback, which
should trigger on the next running of the event loop. I believe that
the TLS code does require a similar callback.
In trying to understand why this is required, imagine a SASL-encrypted
LDAP packet in the following formation:
+-----------------+---------------------+
| SASL Packet #1 | SASL Packet #2 |
----------------------------------------+
| LDAP Packet #1 | LDAP Packet #2 |
----------------------------------------+
In the old code, this was illegal, but it is perfectly standard
SASL-encrypted LDAP. Without the callback, we would read and process
the first LDAP packet, and the SASL code would have read the second SASL
packet (to decrypt enough data for the LDAP packet), and no data would
remain on the socket.
Without data on the socket, read events stop. That is why I add timed
events, until the SASL buffer is drained.
Another approach would be to add a hack to the event system, to have it
pretend there remained data to read off the network (but that is ugly).
In improving the code, to handle more real-world cases, I've been able
to remove almost all the special-cases in the testnonblock code. The
only special case is that we must use a deterministic partial packet
when calling send, rather than a random length. (1 + n/2). This is
needed because of the way the SASL and TLS code works, and the 'resend
on failure' requirements.
Andrew Bartlett
(This used to be commit 5d7c9c12cb2b39673172a357092b80cd814850b0)
2006-07-23 06:50:08 +04:00
void ldapsrv_recv ( struct stream_connection * c , uint16_t flags )
2004-09-13 14:36:59 +04:00
{
2010-09-22 16:24:03 +04:00
smb_panic ( __location__ ) ;
2004-09-13 14:36:59 +04:00
}
2005-11-10 04:41:47 +03:00
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
{
2010-09-22 16:24:03 +04:00
smb_panic ( __location__ ) ;
2006-01-13 03:38:35 +03:00
}
2006-01-13 18:40:15 +03:00
static int ldapsrv_load_limits ( struct ldapsrv_connection * conn )
{
TALLOC_CTX * tmp_ctx ;
const char * attrs [ ] = { " configurationNamingContext " , NULL } ;
const char * attrs2 [ ] = { " lDAPAdminLimits " , NULL } ;
struct ldb_message_element * el ;
struct ldb_result * res = NULL ;
struct ldb_dn * basedn ;
struct ldb_dn * conf_dn ;
struct ldb_dn * policy_dn ;
2009-11-07 23:21:26 +03:00
unsigned int i ;
int ret ;
2006-01-13 18:40:15 +03:00
/* set defaults limits in case of failure */
conn - > limits . initial_timeout = 120 ;
conn - > limits . conn_idle_time = 900 ;
conn - > limits . max_page_size = 1000 ;
conn - > limits . search_timeout = 120 ;
tmp_ctx = talloc_new ( conn ) ;
if ( tmp_ctx = = NULL ) {
return - 1 ;
}
2006-11-22 03:59:34 +03:00
basedn = ldb_dn_new ( tmp_ctx , conn - > ldb , NULL ) ;
if ( ! ldb_dn_validate ( basedn ) ) {
2006-01-13 18:40:15 +03:00
goto failed ;
}
2008-09-23 22:30:06 +04:00
ret = ldb_search ( conn - > ldb , tmp_ctx , & res , basedn , LDB_SCOPE_BASE , attrs , NULL ) ;
2006-12-13 14:19:51 +03:00
if ( ret ! = LDB_SUCCESS ) {
goto failed ;
}
if ( res - > count ! = 1 ) {
2006-01-13 18:40:15 +03:00
goto failed ;
}
2006-11-22 03:59:34 +03:00
conf_dn = ldb_msg_find_attr_as_dn ( conn - > ldb , tmp_ctx , res - > msgs [ 0 ] , " configurationNamingContext " ) ;
2006-01-13 18:40:15 +03:00
if ( conf_dn = = NULL ) {
goto failed ;
}
2006-11-22 03:59:34 +03:00
policy_dn = ldb_dn_copy ( tmp_ctx , conf_dn ) ;
ldb_dn_add_child_fmt ( policy_dn , " CN=Default Query Policy,CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services " ) ;
2006-01-13 18:40:15 +03:00
if ( policy_dn = = NULL ) {
goto failed ;
}
2008-09-23 22:30:06 +04:00
ret = ldb_search ( conn - > ldb , tmp_ctx , & res , policy_dn , LDB_SCOPE_BASE , attrs2 , NULL ) ;
2006-12-13 14:19:51 +03:00
if ( ret ! = LDB_SUCCESS ) {
goto failed ;
}
if ( res - > count ! = 1 ) {
2006-01-13 18:40:15 +03:00
goto failed ;
}
el = ldb_msg_find_element ( res - > msgs [ 0 ] , " lDAPAdminLimits " ) ;
if ( el = = NULL ) {
goto failed ;
}
for ( i = 0 ; i < el - > num_values ; i + + ) {
char policy_name [ 256 ] ;
int policy_value , s ;
2006-03-03 11:23:57 +03:00
s = sscanf ( ( const char * ) el - > values [ i ] . data , " %255[^=]=%d " , policy_name , & policy_value ) ;
2006-01-13 18:40:15 +03:00
if ( ret ! = 2 | | policy_value = = 0 )
continue ;
2009-05-29 11:42:31 +04:00
2006-01-13 18:40:15 +03:00
if ( strcasecmp ( " InitRecvTimeout " , policy_name ) = = 0 ) {
conn - > limits . initial_timeout = policy_value ;
continue ;
}
if ( strcasecmp ( " MaxConnIdleTime " , policy_name ) = = 0 ) {
conn - > limits . conn_idle_time = policy_value ;
continue ;
}
if ( strcasecmp ( " MaxPageSize " , policy_name ) = = 0 ) {
conn - > limits . max_page_size = policy_value ;
continue ;
}
if ( strcasecmp ( " MaxQueryDuration " , policy_name ) = = 0 ) {
conn - > limits . search_timeout = policy_value ;
continue ;
}
}
return 0 ;
failed :
DEBUG ( 0 , ( " Failed to load ldap server query policies \n " ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
2010-09-22 16:24:03 +04:00
static struct tevent_req * ldapsrv_process_call_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tevent_queue * call_queue ,
struct ldapsrv_call * call ) ;
static NTSTATUS ldapsrv_process_call_recv ( struct tevent_req * req ) ;
static bool ldapsrv_call_read_next ( struct ldapsrv_connection * conn ) ;
static void ldapsrv_accept_tls_done ( struct tevent_req * subreq ) ;
2010-09-07 05:57:44 +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
*/
2009-05-29 12:48:54 +04:00
static void ldapsrv_accept ( struct stream_connection * c ,
struct auth_session_info * session_info )
2004-09-13 14:36:59 +04:00
{
2005-06-19 11:21:18 +04:00
struct ldapsrv_service * ldapsrv_service =
2009-02-02 12:30:03 +03:00
talloc_get_type ( c - > private_data , struct ldapsrv_service ) ;
2005-06-19 13:31:34 +04:00
struct ldapsrv_connection * conn ;
2006-01-03 03:10:15 +03:00
struct cli_credentials * server_credentials ;
2006-01-10 01:12:53 +03:00
struct socket_address * socket_address ;
2006-01-03 03:10:15 +03:00
NTSTATUS status ;
2005-06-19 15:10:15 +04:00
int port ;
2010-09-22 16:24:03 +04:00
int ret ;
struct tevent_req * subreq ;
struct timeval endtime ;
2004-09-13 14:36:59 +04:00
2005-06-19 13:31:34 +04:00
conn = talloc_zero ( c , struct ldapsrv_connection ) ;
2005-09-08 15:26:05 +04:00
if ( ! conn ) {
stream_terminate_connection ( c , " ldapsrv_accept: out of memory " ) ;
return ;
}
2004-09-13 14:36:59 +04:00
2010-09-22 16:24:03 +04:00
conn - > sockets . send_queue = tevent_queue_create ( conn , " ldapsev send queue " ) ;
if ( conn - > sockets . send_queue = = NULL ) {
stream_terminate_connection ( c ,
" ldapsrv_accept: tevent_queue_create failed " ) ;
return ;
}
TALLOC_FREE ( c - > event . fde ) ;
ret = tstream_bsd_existing_socket ( conn ,
socket_get_fd ( c - > socket ) ,
& conn - > sockets . raw ) ;
if ( ret = = - 1 ) {
stream_terminate_connection ( c ,
" ldapsrv_accept: out of memory " ) ;
return ;
}
socket_set_flags ( c - > socket , SOCKET_FLAG_NOCLOSE ) ;
2005-06-19 13:31:34 +04:00
conn - > connection = c ;
2005-09-08 15:26:05 +04:00
conn - > service = ldapsrv_service ;
2008-01-06 00:36:33 +03:00
conn - > lp_ctx = ldapsrv_service - > task - > lp_ctx ;
2006-01-03 03:10:15 +03:00
2009-02-02 12:30:03 +03:00
c - > private_data = conn ;
2005-06-19 11:21:18 +04:00
2006-01-10 01:12:53 +03:00
socket_address = socket_get_my_addr ( c - > socket , conn ) ;
if ( ! socket_address ) {
ldapsrv_terminate_connection ( conn , " ldapsrv_accept: failed to obtain local socket address! " ) ;
return ;
}
port = socket_address - > port ;
talloc_free ( socket_address ) ;
2010-09-22 16:24:03 +04:00
if ( port = = 3268 ) /* Global catalog */ {
2007-10-07 01:42:58 +04:00
conn - > global_catalog = true ;
2005-09-08 15:26:05 +04:00
}
2010-09-07 05:57:44 +04:00
2007-12-02 19:56:09 +03:00
server_credentials = cli_credentials_init ( conn ) ;
2006-04-29 13:20:22 +04:00
if ( ! server_credentials ) {
stream_terminate_connection ( c , " Failed to init server credentials \n " ) ;
return ;
}
2009-05-29 11:42:31 +04:00
2007-12-03 23:25:06 +03:00
cli_credentials_set_conf ( server_credentials , conn - > lp_ctx ) ;
2007-12-14 00:46:17 +03:00
status = cli_credentials_set_machine_account ( server_credentials , conn - > lp_ctx ) ;
2006-04-29 13:20:22 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
stream_terminate_connection ( c , talloc_asprintf ( conn , " Failed to obtain server credentials, perhaps a standalone server?: %s \n " , nt_errstr ( status ) ) ) ;
return ;
}
conn - > server_credentials = server_credentials ;
2005-11-10 04:41:47 +03:00
2009-05-29 12:48:54 +04:00
conn - > session_info = talloc_move ( conn , & session_info ) ;
2005-10-07 15:31:45 +04:00
2006-01-13 03:38:35 +03:00
if ( ! NT_STATUS_IS_OK ( ldapsrv_backend_Init ( conn ) ) ) {
ldapsrv_terminate_connection ( conn , " backend Init failed " ) ;
2005-12-08 13:23:56 +03:00
return ;
2005-10-07 15:31:45 +04:00
}
2006-01-13 18:40:15 +03:00
/* load limits from the conf partition */
ldapsrv_load_limits ( conn ) ; /* should we fail on error ? */
2005-10-07 15:31:45 +04:00
2006-01-13 03:38:35 +03:00
/* register the server */
2005-07-10 05:08:10 +04:00
irpc_add_name ( c - > msg_ctx , " ldap_server " ) ;
2006-01-13 03:38:35 +03:00
2010-09-22 16:24:03 +04:00
conn - > sockets . active = conn - > sockets . raw ;
if ( port ! = 636 ) {
ldapsrv_call_read_next ( conn ) ;
return ;
}
endtime = timeval_current_ofs ( conn - > limits . conn_idle_time , 0 ) ;
subreq = tstream_tls_accept_send ( conn ,
conn - > connection - > event . ctx ,
conn - > sockets . raw ,
conn - > service - > tls_params ) ;
if ( subreq = = NULL ) {
ldapsrv_terminate_connection ( conn , " ldapsrv_accept: "
" no memory for tstream_tls_accept_send " ) ;
return ;
}
tevent_req_set_endtime ( subreq ,
conn - > connection - > event . ctx ,
endtime ) ;
tevent_req_set_callback ( subreq , ldapsrv_accept_tls_done , conn ) ;
}
static void ldapsrv_accept_tls_done ( struct tevent_req * subreq )
{
struct ldapsrv_connection * conn =
tevent_req_callback_data ( subreq ,
struct ldapsrv_connection ) ;
int ret ;
int sys_errno ;
ret = tstream_tls_accept_recv ( subreq , & sys_errno ,
conn , & conn - > sockets . tls ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
const char * reason ;
reason = talloc_asprintf ( conn , " ldapsrv_accept_tls_loop: "
" tstream_tls_accept_recv() - %d:%s " ,
sys_errno , strerror ( sys_errno ) ) ;
if ( ! reason ) {
reason = " ldapsrv_accept_tls_loop: "
" tstream_tls_accept_recv() - failed " ;
}
ldapsrv_terminate_connection ( conn , reason ) ;
return ;
}
2010-09-22 16:24:03 +04:00
2010-09-22 16:24:03 +04:00
conn - > sockets . active = conn - > sockets . tls ;
ldapsrv_call_read_next ( conn ) ;
}
2010-09-22 16:24:03 +04:00
2010-09-22 16:24:03 +04:00
static void ldapsrv_call_read_done ( struct tevent_req * subreq ) ;
static bool ldapsrv_call_read_next ( struct ldapsrv_connection * conn )
{
struct tevent_req * subreq ;
if ( timeval_is_zero ( & conn - > limits . endtime ) ) {
conn - > limits . endtime =
timeval_current_ofs ( conn - > limits . initial_timeout , 0 ) ;
} else {
conn - > limits . endtime =
timeval_current_ofs ( conn - > limits . conn_idle_time , 0 ) ;
}
/*
* The minimun size of a LDAP pdu is 7 bytes
*
* dumpasn1 - hh ldap - unbind - min . dat
*
* < 30 05 02 01 09 42 00 >
* 0 5 : SEQUENCE {
* < 02 01 09 >
* 2 1 : INTEGER 9
* < 42 00 >
* 5 0 : [ APPLICATION 2 ]
* : Error : Object has zero length .
* : }
*
* dumpasn1 - hh ldap - unbind - windows . dat
*
* < 30 84 00 00 00 05 02 01 09 42 00 >
* 0 5 : SEQUENCE {
* < 02 01 09 >
* 6 1 : INTEGER 9
* < 42 00 >
* 9 0 : [ APPLICATION 2 ]
* : Error : Object has zero length .
* : }
*
* This means using an initial read size
* of 7 is ok .
*/
subreq = tstream_read_pdu_blob_send ( conn ,
conn - > connection - > event . ctx ,
conn - > sockets . active ,
7 , /* initial_read_size */
ldap_full_packet ,
conn ) ;
if ( subreq = = NULL ) {
ldapsrv_terminate_connection ( conn , " ldapsrv_call_read_next: "
" no memory for tstream_read_pdu_blob_send " ) ;
return false ;
}
tevent_req_set_endtime ( subreq ,
conn - > connection - > event . ctx ,
conn - > limits . endtime ) ;
tevent_req_set_callback ( subreq , ldapsrv_call_read_done , conn ) ;
return true ;
}
static void ldapsrv_call_process_done ( struct tevent_req * subreq ) ;
static void ldapsrv_call_read_done ( struct tevent_req * subreq )
{
struct ldapsrv_connection * conn =
tevent_req_callback_data ( subreq ,
struct ldapsrv_connection ) ;
NTSTATUS status ;
struct ldapsrv_call * call ;
struct asn1_data * asn1 ;
DATA_BLOB blob ;
call = talloc_zero ( conn , struct ldapsrv_call ) ;
if ( ! call ) {
ldapsrv_terminate_connection ( conn , " no memory " ) ;
return ;
}
call - > conn = conn ;
status = tstream_read_pdu_blob_recv ( subreq ,
call ,
& blob ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
const char * reason ;
reason = talloc_asprintf ( call , " ldapsrv_call_loop: "
" tstream_read_pdu_blob_recv() - %s " ,
nt_errstr ( status ) ) ;
if ( ! reason ) {
reason = nt_errstr ( status ) ;
}
ldapsrv_terminate_connection ( conn , reason ) ;
return ;
}
asn1 = asn1_init ( call ) ;
if ( asn1 = = NULL ) {
ldapsrv_terminate_connection ( conn , " no memory " ) ;
return ;
}
call - > request = talloc ( call , struct ldap_message ) ;
if ( call - > request = = NULL ) {
ldapsrv_terminate_connection ( conn , " no memory " ) ;
return ;
}
if ( ! asn1_load ( asn1 , blob ) ) {
ldapsrv_terminate_connection ( conn , " asn1_load failed " ) ;
return ;
}
status = ldap_decode ( asn1 , samba_ldap_control_handlers ( ) ,
call - > request ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldapsrv_terminate_connection ( conn , nt_errstr ( status ) ) ;
return ;
}
data_blob_free ( & blob ) ;
/* queue the call in the global queue */
subreq = ldapsrv_process_call_send ( call ,
conn - > connection - > event . ctx ,
conn - > service - > call_queue ,
call ) ;
if ( subreq = = NULL ) {
ldapsrv_terminate_connection ( conn , " ldapsrv_process_call_send failed " ) ;
return ;
}
tevent_req_set_callback ( subreq , ldapsrv_call_process_done , call ) ;
conn - > active_call = subreq ;
}
static void ldapsrv_call_writev_done ( struct tevent_req * subreq ) ;
static void ldapsrv_call_process_done ( struct tevent_req * subreq )
{
struct ldapsrv_call * call =
tevent_req_callback_data ( subreq ,
struct ldapsrv_call ) ;
struct ldapsrv_connection * conn = call - > conn ;
NTSTATUS status ;
DATA_BLOB blob = data_blob_null ;
conn - > active_call = NULL ;
status = ldapsrv_process_call_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldapsrv_terminate_connection ( conn , nt_errstr ( status ) ) ;
return ;
}
/* build all the replies into a single blob */
while ( call - > replies ) {
DATA_BLOB b ;
bool ret ;
if ( ! ldap_encode ( call - > replies - > msg , samba_ldap_control_handlers ( ) , & b , call ) ) {
DEBUG ( 0 , ( " Failed to encode ldap reply of type %d \n " ,
call - > replies - > msg - > type ) ) ;
ldapsrv_terminate_connection ( conn , " ldap_encode failed " ) ;
return ;
}
ret = data_blob_append ( call , & blob , b . data , b . length ) ;
data_blob_free ( & b ) ;
talloc_set_name_const ( blob . data , " Outgoing, encoded LDAP packet " ) ;
if ( ! ret ) {
ldapsrv_terminate_connection ( conn , " data_blob_append failed " ) ;
return ;
}
DLIST_REMOVE ( call - > replies , call - > replies ) ;
}
if ( blob . length = = 0 ) {
TALLOC_FREE ( call ) ;
ldapsrv_call_read_next ( conn ) ;
return ;
}
call - > out_iov . iov_base = blob . data ;
call - > out_iov . iov_len = blob . length ;
subreq = tstream_writev_queue_send ( call ,
conn - > connection - > event . ctx ,
conn - > sockets . active ,
conn - > sockets . send_queue ,
& call - > out_iov , 1 ) ;
if ( subreq = = NULL ) {
ldapsrv_terminate_connection ( conn , " stream_writev_queue_send failed " ) ;
return ;
}
tevent_req_set_callback ( subreq , ldapsrv_call_writev_done , call ) ;
}
static void ldapsrv_call_postprocess_done ( struct tevent_req * subreq ) ;
static void ldapsrv_call_writev_done ( struct tevent_req * subreq )
{
struct ldapsrv_call * call =
tevent_req_callback_data ( subreq ,
struct ldapsrv_call ) ;
struct ldapsrv_connection * conn = call - > conn ;
int sys_errno ;
int rc ;
rc = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( rc = = - 1 ) {
const char * reason ;
reason = talloc_asprintf ( call , " ldapsrv_call_writev_done: "
" tstream_writev_queue_recv() - %d:%s " ,
sys_errno , strerror ( sys_errno ) ) ;
if ( reason = = NULL ) {
reason = " ldapsrv_call_writev_done: "
" tstream_writev_queue_recv() failed " ;
}
ldapsrv_terminate_connection ( conn , reason ) ;
return ;
}
if ( call - > postprocess_send ) {
subreq = call - > postprocess_send ( call ,
conn - > connection - > event . ctx ,
call - > postprocess_private ) ;
if ( subreq = = NULL ) {
ldapsrv_terminate_connection ( conn , " ldapsrv_call_writev_done: "
" call->postprocess_send - no memory " ) ;
return ;
}
tevent_req_set_callback ( subreq ,
ldapsrv_call_postprocess_done ,
call ) ;
return ;
}
TALLOC_FREE ( call ) ;
ldapsrv_call_read_next ( conn ) ;
}
static void ldapsrv_call_postprocess_done ( struct tevent_req * subreq )
{
struct ldapsrv_call * call =
tevent_req_callback_data ( subreq ,
struct ldapsrv_call ) ;
struct ldapsrv_connection * conn = call - > conn ;
NTSTATUS status ;
status = call - > postprocess_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
const char * reason ;
reason = talloc_asprintf ( call , " ldapsrv_call_postprocess_done: "
" call->postprocess_recv() - %s " ,
nt_errstr ( status ) ) ;
if ( reason = = NULL ) {
reason = nt_errstr ( status ) ;
}
ldapsrv_terminate_connection ( conn , reason ) ;
return ;
}
TALLOC_FREE ( call ) ;
ldapsrv_call_read_next ( conn ) ;
}
struct ldapsrv_process_call_state {
struct ldapsrv_call * call ;
} ;
static void ldapsrv_process_call_trigger ( struct tevent_req * req ,
void * private_data ) ;
static struct tevent_req * ldapsrv_process_call_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tevent_queue * call_queue ,
struct ldapsrv_call * call )
{
struct tevent_req * req ;
struct ldapsrv_process_call_state * state ;
bool ok ;
req = tevent_req_create ( mem_ctx , & state ,
struct ldapsrv_process_call_state ) ;
if ( req = = NULL ) {
return req ;
}
state - > call = call ;
ok = tevent_queue_add ( call_queue , ev , req ,
ldapsrv_process_call_trigger , NULL ) ;
if ( ! ok ) {
tevent_req_nomem ( NULL , req ) ;
return tevent_req_post ( req , ev ) ;
}
return req ;
}
static void ldapsrv_process_call_trigger ( struct tevent_req * req ,
void * private_data )
{
struct ldapsrv_process_call_state * state =
tevent_req_data ( req ,
struct ldapsrv_process_call_state ) ;
NTSTATUS status ;
/* make the call */
status = ldapsrv_do_call ( state - > call ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
tevent_req_done ( req ) ;
}
static NTSTATUS ldapsrv_process_call_recv ( struct tevent_req * req )
{
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
2004-09-13 14:36:59 +04:00
}
2009-05-29 12:48:54 +04:00
static void ldapsrv_accept_nonpriv ( struct stream_connection * c )
{
struct ldapsrv_service * ldapsrv_service = talloc_get_type_abort (
c - > private_data , struct ldapsrv_service ) ;
struct auth_session_info * session_info ;
NTSTATUS status ;
status = auth_anonymous_session_info (
2010-04-09 11:18:53 +04:00
c , ldapsrv_service - > task - > lp_ctx , & session_info ) ;
2009-05-29 12:48:54 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
stream_terminate_connection ( c , " failed to setup anonymous "
" session info " ) ;
return ;
}
ldapsrv_accept ( c , session_info ) ;
}
static const struct stream_server_ops ldap_stream_nonpriv_ops = {
. name = " ldap " ,
. accept_connection = ldapsrv_accept_nonpriv ,
. recv_handler = ldapsrv_recv ,
. send_handler = ldapsrv_send ,
} ;
2009-06-18 08:55:31 +04:00
/* The feature removed behind an #ifdef until we can do it properly
* with an EXTERNAL bind . */
2009-06-19 09:29:42 +04:00
# define WITH_LDAPI_PRIV_SOCKET
2009-06-18 08:55:31 +04:00
# ifdef WITH_LDAPI_PRIV_SOCKET
2009-05-29 12:48:54 +04:00
static void ldapsrv_accept_priv ( struct stream_connection * c )
{
struct ldapsrv_service * ldapsrv_service = talloc_get_type_abort (
c - > private_data , struct ldapsrv_service ) ;
struct auth_session_info * session_info ;
2009-10-25 09:19:03 +03:00
session_info = system_session ( ldapsrv_service - > task - > lp_ctx ) ;
if ( ! session_info ) {
2009-05-29 12:48:54 +04:00
stream_terminate_connection ( c , " failed to setup system "
" session info " ) ;
return ;
}
ldapsrv_accept ( c , session_info ) ;
}
static const struct stream_server_ops ldap_stream_priv_ops = {
2005-01-14 04:32:56 +03:00
. name = " ldap " ,
2009-05-29 12:48:54 +04:00
. accept_connection = ldapsrv_accept_priv ,
2005-01-14 04:32:56 +03:00
. recv_handler = ldapsrv_recv ,
. send_handler = ldapsrv_send ,
} ;
2009-06-18 08:55:31 +04:00
# endif
2005-01-30 03:54:57 +03:00
/*
add a socket address to the list of events , one event per port
*/
2008-12-29 22:24:57 +03:00
static NTSTATUS add_socket ( struct tevent_context * event_context ,
2010-07-16 08:32:42 +04:00
struct loadparm_context * lp_ctx ,
2005-06-19 13:31:34 +04:00
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 ;
2006-12-13 14:19:51 +03:00
struct ldb_context * ldb ;
2005-01-30 03:54:57 +03:00
2008-01-06 04:03:43 +03:00
status = stream_setup_socket ( event_context , lp_ctx ,
2009-05-29 12:48:54 +04:00
model_ops , & ldap_stream_nonpriv_ops ,
2007-12-06 18:54:34 +03:00
" ipv4 " , address , & port ,
2010-07-16 08:32:42 +04:00
lpcfg_socket_options ( lp_ctx ) ,
2007-12-06 18:54:34 +03:00
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 ) ) ) ;
2010-06-28 11:57:33 +04:00
return status ;
2005-06-19 11:21:18 +04:00
}
2005-01-30 03:54:57 +03:00
2010-09-22 16:24:03 +04:00
if ( tstream_tls_params_enabled ( ldap_service - > tls_params ) ) {
2005-06-19 13:31:34 +04:00
/* add ldaps server */
port = 636 ;
2010-07-16 08:32:42 +04:00
status = stream_setup_socket ( event_context , lp_ctx ,
2009-05-29 12:48:54 +04:00
model_ops ,
& ldap_stream_nonpriv_ops ,
2007-12-06 18:54:34 +03:00
" ipv4 " , address , & port ,
2010-07-16 08:32:42 +04:00
lpcfg_socket_options ( lp_ctx ) ,
2007-12-06 18:54:34 +03:00
ldap_service ) ;
2005-06-19 13:31:34 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " ldapsrv failed to bind to %s:%u - %s \n " ,
address , port , nt_errstr ( status ) ) ) ;
2010-06-28 11:57:33 +04:00
return status ;
2005-06-19 13:31:34 +04:00
}
2005-06-19 11:21:18 +04:00
}
2005-06-19 13:31:34 +04:00
2008-07-16 05:11:25 +04:00
/* Load LDAP database, but only to read our settings */
2008-04-17 14:23:44 +04:00
ldb = samdb_connect ( ldap_service , ldap_service - > task - > event_ctx ,
2010-10-10 19:00:45 +04:00
lp_ctx , system_session ( lp_ctx ) , 0 ) ;
2006-12-13 14:19:51 +03:00
if ( ! ldb ) {
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
2009-05-29 11:42:31 +04:00
2008-01-03 13:40:24 +03:00
if ( samdb_is_gc ( ldb ) ) {
2005-10-17 15:32:20 +04:00
port = 3268 ;
2008-01-06 04:03:43 +03:00
status = stream_setup_socket ( event_context , lp_ctx ,
2009-05-29 12:48:54 +04:00
model_ops ,
& ldap_stream_nonpriv_ops ,
2007-12-06 18:54:34 +03:00
" ipv4 " , address , & port ,
2010-07-16 08:32:42 +04:00
lpcfg_socket_options ( lp_ctx ) ,
2007-12-06 18:54:34 +03:00
ldap_service ) ;
2005-10-17 15:32:20 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " ldapsrv failed to bind to %s:%u - %s \n " ,
address , port , nt_errstr ( status ) ) ) ;
2010-06-28 11:57:33 +04:00
return status ;
2005-10-17 15:32:20 +04:00
}
}
2008-07-16 05:11:25 +04:00
/* And once we are bound, free the tempoary ldb, it will
* connect again on each incoming LDAP connection */
2009-10-23 07:27:00 +04:00
talloc_unlink ( ldap_service , ldb ) ;
2008-07-16 05:11:25 +04:00
2010-06-28 11:57:33 +04:00
return NT_STATUS_OK ;
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
{
2009-06-18 08:55:31 +04:00
char * ldapi_path ;
# ifdef WITH_LDAPI_PRIV_SOCKET
char * priv_dir ;
# endif
2010-09-22 16:24:03 +04:00
const char * dns_host_name ;
2005-01-30 03:54:57 +03:00
struct ldapsrv_service * ldap_service ;
NTSTATUS status ;
2006-08-21 05:25:20 +04:00
const struct model_ops * model_ops ;
2004-09-13 14:36:59 +04:00
2010-07-16 08:32:42 +04:00
switch ( lpcfg_server_role ( task - > lp_ctx ) ) {
2007-09-22 16:57:17 +04:00
case ROLE_STANDALONE :
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " ldap_server: no LDAP server required in standalone configuration " ,
false ) ;
2007-09-22 16:57:17 +04:00
return ;
case ROLE_DOMAIN_MEMBER :
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " ldap_server: no LDAP server required in member server configuration " ,
false ) ;
2007-09-22 16:57:17 +04:00
return ;
case ROLE_DOMAIN_CONTROLLER :
/* Yes, we want an LDAP server */
break ;
}
2006-03-09 20:48:41 +03:00
task_server_set_title ( task , " task[ldapsrv] " ) ;
2006-08-21 05:25:20 +04:00
/* run the ldap server as a single process */
2008-09-22 20:15:24 +04:00
model_ops = process_model_startup ( task - > event_ctx , " single " ) ;
2006-08-21 05:25:20 +04:00
if ( ! model_ops ) goto failed ;
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
2008-01-06 00:36:33 +03:00
ldap_service - > task = task ;
2010-09-22 16:24:03 +04:00
dns_host_name = talloc_asprintf ( ldap_service , " %s.%s " ,
lpcfg_netbios_name ( task - > lp_ctx ) ,
lpcfg_dnsdomain ( task - > lp_ctx ) ) ;
if ( dns_host_name = = NULL ) goto failed ;
status = tstream_tls_params_server ( ldap_service ,
dns_host_name ,
lpcfg_tls_enabled ( task - > lp_ctx ) ,
lpcfg_tls_keyfile ( ldap_service , task - > lp_ctx ) ,
lpcfg_tls_certfile ( ldap_service , task - > lp_ctx ) ,
lpcfg_tls_cafile ( ldap_service , task - > lp_ctx ) ,
lpcfg_tls_crlfile ( ldap_service , task - > lp_ctx ) ,
lpcfg_tls_dhpfile ( ldap_service , task - > lp_ctx ) ,
& ldap_service - > tls_params ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " ldapsrv failed tstream_tls_patams_server - %s \n " ,
nt_errstr ( status ) ) ) ;
goto failed ;
}
ldap_service - > call_queue = tevent_queue_create ( ldap_service , " ldapsrv_call_queue " ) ;
if ( ldap_service - > call_queue = = NULL ) goto failed ;
2005-01-30 03:54:57 +03:00
2010-07-16 08:32:42 +04:00
if ( lpcfg_interfaces ( task - > lp_ctx ) & & lpcfg_bind_interfaces_only ( task - > lp_ctx ) ) {
2007-12-12 00:23:14 +03:00
struct interface * ifaces ;
int num_interfaces ;
2005-01-30 03:54:57 +03:00
int i ;
2010-07-16 08:32:42 +04:00
load_interfaces ( task , lpcfg_interfaces ( task - > lp_ctx ) , & ifaces ) ;
2007-12-12 00:23:14 +03:00
num_interfaces = iface_count ( ifaces ) ;
2005-01-30 03:54:57 +03:00
/* 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 + + ) {
2007-12-12 00:23:14 +03:00
const char * address = iface_n_ip ( ifaces , i ) ;
2007-12-02 19:09:52 +03:00
status = add_socket ( task - > event_ctx , task - > lp_ctx , model_ops , address , ldap_service ) ;
2005-06-19 11:21:18 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
2005-01-30 03:54:57 +03:00
}
} else {
2010-07-16 08:32:42 +04:00
status = add_socket ( task - > event_ctx , task - > lp_ctx , model_ops ,
lpcfg_socket_address ( task - > lp_ctx ) , ldap_service ) ;
2005-06-19 11:21:18 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
2005-01-30 03:54:57 +03:00
}
2007-12-02 19:09:52 +03:00
ldapi_path = private_path ( ldap_service , task - > lp_ctx , " ldapi " ) ;
2007-11-10 07:31:26 +03:00
if ( ! ldapi_path ) {
goto failed ;
}
2008-01-06 04:03:43 +03:00
status = stream_setup_socket ( task - > event_ctx , task - > lp_ctx ,
2009-05-29 12:48:54 +04:00
model_ops , & ldap_stream_nonpriv_ops ,
2007-12-06 18:54:34 +03:00
" unix " , ldapi_path , NULL ,
2010-07-16 08:32:42 +04:00
lpcfg_socket_options ( task - > lp_ctx ) ,
2007-12-06 18:54:34 +03:00
ldap_service ) ;
2007-11-10 07:31:26 +03:00
talloc_free ( ldapi_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " ldapsrv failed to bind to %s - %s \n " ,
ldapi_path , nt_errstr ( status ) ) ) ;
}
2009-06-18 08:55:31 +04:00
# ifdef WITH_LDAPI_PRIV_SOCKET
2009-05-29 12:48:54 +04:00
priv_dir = private_path ( ldap_service , task - > lp_ctx , " ldap_priv " ) ;
if ( priv_dir = = NULL ) {
goto failed ;
}
/*
* Make sure the directory for the privileged ldapi socket exists , and
* is of the correct permissions
*/
if ( ! directory_create_or_exist ( priv_dir , geteuid ( ) , 0750 ) ) {
task_server_terminate ( task , " Cannot create ldap "
2009-09-19 05:05:55 +04:00
" privileged ldapi directory " , true ) ;
2009-05-29 12:48:54 +04:00
return ;
}
ldapi_path = talloc_asprintf ( ldap_service , " %s/ldapi " , priv_dir ) ;
talloc_free ( priv_dir ) ;
if ( ldapi_path = = NULL ) {
goto failed ;
}
status = stream_setup_socket ( task - > event_ctx , task - > lp_ctx ,
model_ops , & ldap_stream_priv_ops ,
" unix " , ldapi_path , NULL ,
2010-07-16 08:32:42 +04:00
lpcfg_socket_options ( task - > lp_ctx ) ,
2009-05-29 12:48:54 +04:00
ldap_service ) ;
talloc_free ( ldapi_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " ldapsrv failed to bind to %s - %s \n " ,
ldapi_path , nt_errstr ( status ) ) ) ;
}
2009-06-18 08:55:31 +04:00
# endif
2005-06-19 11:21:18 +04:00
return ;
failed :
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " Failed to startup ldap server task " , true ) ;
2005-06-19 11:21:18 +04:00
}
2005-01-30 03:54:57 +03:00
2004-09-13 14:36:59 +04:00
NTSTATUS server_service_ldap_init ( void )
{
2008-02-04 13:58:29 +03:00
return register_server_service ( " ldap " , ldapsrv_task_init ) ;
2004-09-13 14:36:59 +04:00
}