2004-10-08 16:31:13 +04:00
/*
Unix SMB / CIFS implementation .
LDAP server
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-10-08 16:31:13 +04:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-10-08 16:31:13 +04:00
*/
# include "includes.h"
2004-11-02 09:52:59 +03:00
# include "ldap_server/ldap_server.h"
2005-01-01 01:45:11 +03:00
# include "auth/auth.h"
2020-11-20 17:27:17 +03:00
# include "samba/service.h"
2011-02-10 06:12:51 +03:00
# include <ldb.h>
# include <ldb_errors.h>
2017-06-13 16:02:41 +03:00
# include "../lib/util/dlinklist.h"
2005-12-28 18:38:36 +03:00
# include "dsdb/samdb/samdb.h"
2006-11-07 03:48:36 +03:00
# include "auth/gensec/gensec.h"
2010-09-22 16:24:03 +04:00
# include "auth/gensec/gensec_tstream.h"
2024-01-23 16:20:24 +03:00
# include "lib/tls/tls.h"
2007-12-02 19:56:09 +03:00
# include "param/param.h"
2010-09-22 16:24:03 +04:00
# include "../lib/util/tevent_ntstatus.h"
2020-08-07 14:40:58 +03:00
# include "lib/util/time_basic.h"
2004-10-08 16:31:13 +04:00
2024-07-05 15:21:33 +03:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_LDAPSRV
2017-02-24 20:30:56 +03:00
static char * ldapsrv_bind_error_msg ( TALLOC_CTX * mem_ctx ,
HRESULT hresult ,
uint32_t DSID ,
NTSTATUS status )
{
WERROR werr ;
char * msg = NULL ;
status = nt_status_squash ( status ) ;
werr = ntstatus_to_werror ( status ) ;
/*
* There are 4 lower case hex digits following ' v ' at the end ,
* but different Windows Versions return different values :
*
* Windows 2008 R2 uses ' v1db1 '
* Windows 2012 R2 uses ' v2580 '
*
* We just match Windows 2008 R2 as that ' s what was referenced
* in https : //bugzilla.samba.org/show_bug.cgi?id=9048
*/
msg = talloc_asprintf ( mem_ctx , " %08X: LdapErr: DSID-%08X, comment: "
" AcceptSecurityContext error, data %x, v1db1 " ,
( unsigned ) HRES_ERROR_V ( hresult ) ,
( unsigned ) DSID ,
( unsigned ) W_ERROR_V ( werr ) ) ;
return msg ;
}
2017-05-11 19:04:15 +03:00
struct ldapsrv_bind_wait_context {
struct ldapsrv_reply * reply ;
struct tevent_req * req ;
NTSTATUS status ;
bool done ;
} ;
2017-02-24 20:30:56 +03:00
2017-05-11 19:04:15 +03:00
struct ldapsrv_bind_wait_state {
uint8_t dummy ;
} ;
static struct tevent_req * ldapsrv_bind_wait_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
void * private_data )
2004-10-08 16:31:13 +04:00
{
2017-05-11 19:04:15 +03:00
struct ldapsrv_bind_wait_context * bind_wait =
talloc_get_type_abort ( private_data ,
struct ldapsrv_bind_wait_context ) ;
struct tevent_req * req ;
struct ldapsrv_bind_wait_state * state ;
2004-10-08 16:31:13 +04:00
2017-05-11 19:04:15 +03:00
req = tevent_req_create ( mem_ctx , & state ,
struct ldapsrv_bind_wait_state ) ;
if ( req = = NULL ) {
return NULL ;
}
bind_wait - > req = req ;
2005-12-19 09:56:45 +03:00
2017-05-11 19:04:15 +03:00
tevent_req_defer_callback ( req , ev ) ;
2005-12-19 09:56:45 +03:00
2017-05-11 19:04:15 +03:00
if ( ! bind_wait - > done ) {
return req ;
}
if ( tevent_req_nterror ( req , bind_wait - > status ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
static NTSTATUS ldapsrv_bind_wait_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
}
static NTSTATUS ldapsrv_bind_wait_setup ( struct ldapsrv_call * call ,
struct ldapsrv_reply * reply )
{
struct ldapsrv_bind_wait_context * bind_wait = NULL ;
if ( call - > wait_private ! = NULL ) {
return NT_STATUS_INTERNAL_ERROR ;
}
bind_wait = talloc_zero ( call , struct ldapsrv_bind_wait_context ) ;
if ( bind_wait = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
bind_wait - > reply = reply ;
call - > wait_private = bind_wait ;
call - > wait_send = ldapsrv_bind_wait_send ;
call - > wait_recv = ldapsrv_bind_wait_recv ;
return NT_STATUS_OK ;
}
static void ldapsrv_bind_wait_finished ( struct ldapsrv_call * call ,
NTSTATUS status )
{
struct ldapsrv_bind_wait_context * bind_wait =
talloc_get_type_abort ( call - > wait_private ,
struct ldapsrv_bind_wait_context ) ;
2005-12-19 09:56:45 +03:00
2017-05-11 19:04:15 +03:00
bind_wait - > done = true ;
bind_wait - > status = status ;
if ( bind_wait - > req = = NULL ) {
return ;
}
if ( tevent_req_nterror ( bind_wait - > req , status ) ) {
return ;
}
tevent_req_done ( bind_wait - > req ) ;
}
static void ldapsrv_BindSimple_done ( struct tevent_req * subreq ) ;
static NTSTATUS ldapsrv_BindSimple ( struct ldapsrv_call * call )
{
struct ldap_BindRequest * req = & call - > request - > r . BindRequest ;
struct ldapsrv_reply * reply = NULL ;
struct ldap_BindResponse * resp = NULL ;
int result ;
const char * errstr = NULL ;
NTSTATUS status ;
2017-03-06 04:10:17 +03:00
bool using_tls = call - > conn - > sockets . active = = call - > conn - > sockets . tls ;
2017-05-11 19:04:15 +03:00
struct tevent_req * subreq = NULL ;
2017-03-06 04:10:17 +03:00
2004-10-10 02:00:00 +04:00
DEBUG ( 10 , ( " BindSimple dn: %s \n " , req - > dn ) ) ;
2004-10-08 16:31:13 +04:00
2015-08-28 13:19:37 +03:00
reply = ldapsrv_init_reply ( call , LDAP_TAG_BindResponse ) ;
if ( ! reply ) {
return NT_STATUS_NO_MEMORY ;
}
if ( req - > dn ! = NULL & &
strlen ( req - > dn ) ! = 0 & &
call - > conn - > require_strong_auth > LDAP_SERVER_REQUIRE_STRONG_AUTH_NO & &
2017-03-06 04:10:17 +03:00
! using_tls )
2015-08-28 13:19:37 +03:00
{
status = NT_STATUS_NETWORK_ACCESS_DENIED ;
result = LDAP_STRONG_AUTH_REQUIRED ;
errstr = talloc_asprintf ( reply ,
" BindSimple: Transport encryption required. " ) ;
goto do_reply ;
}
2017-05-11 19:04:15 +03:00
subreq = authenticate_ldap_simple_bind_send ( call ,
call - > conn - > connection - > event . ctx ,
call - > conn - > connection - > msg_ctx ,
call - > conn - > lp_ctx ,
call - > conn - > connection - > remote_address ,
call - > conn - > connection - > local_address ,
using_tls ,
req - > dn ,
req - > creds . password ) ;
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq , ldapsrv_BindSimple_done , call ) ;
status = ldapsrv_bind_wait_setup ( call , reply ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( subreq ) ;
return status ;
}
2005-12-19 09:56:45 +03:00
2017-05-11 19:04:15 +03:00
/*
* The rest will be async .
*/
return NT_STATUS_OK ;
do_reply :
resp = & reply - > msg - > r . BindResponse ;
resp - > response . resultcode = result ;
resp - > response . errormessage = errstr ;
resp - > response . dn = NULL ;
resp - > response . referral = NULL ;
resp - > SASL . secblob = NULL ;
ldapsrv_queue_reply ( call , reply ) ;
return NT_STATUS_OK ;
}
static void ldapsrv_BindSimple_done ( struct tevent_req * subreq )
{
struct ldapsrv_call * call =
tevent_req_callback_data ( subreq ,
struct ldapsrv_call ) ;
struct ldapsrv_bind_wait_context * bind_wait =
talloc_get_type_abort ( call - > wait_private ,
struct ldapsrv_bind_wait_context ) ;
struct ldapsrv_reply * reply = bind_wait - > reply ;
struct auth_session_info * session_info = NULL ;
NTSTATUS status ;
struct ldap_BindResponse * resp = NULL ;
int result ;
const char * errstr = NULL ;
status = authenticate_ldap_simple_bind_recv ( subreq ,
call ,
& session_info ) ;
2005-12-19 09:56:45 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
2017-09-14 06:07:10 +03:00
char * ldb_errstring = NULL ;
2005-12-19 09:56:45 +03:00
result = LDAP_SUCCESS ;
errstr = NULL ;
2010-08-18 04:00:40 +04:00
talloc_unlink ( call - > conn , call - > conn - > session_info ) ;
2011-08-01 09:39:01 +04:00
call - > conn - > session_info = talloc_steal ( call - > conn , session_info ) ;
2006-01-13 03:38:35 +03:00
2017-03-03 02:53:06 +03:00
call - > conn - > authz_logged = true ;
2006-01-13 03:38:35 +03:00
/* don't leak the old LDB */
2010-08-18 04:00:40 +04:00
talloc_unlink ( call - > conn , call - > conn - > ldb ) ;
2006-01-13 03:38:35 +03:00
2017-09-14 06:07:10 +03:00
result = ldapsrv_backend_Init ( call - > conn , & ldb_errstring ) ;
if ( result ! = LDB_SUCCESS ) {
/* Only put the detailed error in DEBUG() */
2023-08-07 07:52:59 +03:00
DBG_ERR ( " ldapsrv_backend_Init failed: %s: %s \n " ,
2017-09-14 06:07:10 +03:00
ldb_errstring , ldb_strerror ( result ) ) ;
errstr = talloc_strdup ( reply ,
" Simple Bind: Failed to advise "
" ldb new credentials " ) ;
result = LDB_ERR_OPERATIONS_ERROR ;
2005-12-19 09:56:45 +03:00
}
} else {
2011-03-03 03:05:33 +03:00
status = nt_status_squash ( status ) ;
2005-12-19 09:56:45 +03:00
result = LDAP_INVALID_CREDENTIALS ;
2017-02-24 20:30:56 +03:00
errstr = ldapsrv_bind_error_msg ( reply , HRES_SEC_E_INVALID_TOKEN ,
0x0C0903A9 , status ) ;
2005-12-19 09:56:45 +03:00
}
2005-06-15 04:27:51 +04:00
resp = & reply - > msg - > r . BindResponse ;
2005-12-19 09:56:45 +03:00
resp - > response . resultcode = result ;
resp - > response . errormessage = errstr ;
2004-10-08 16:31:13 +04:00
resp - > response . dn = NULL ;
resp - > response . referral = NULL ;
2006-02-15 18:19:10 +03:00
resp - > SASL . secblob = NULL ;
2004-10-08 16:31:13 +04:00
2005-06-19 13:31:34 +04:00
ldapsrv_queue_reply ( call , reply ) ;
2017-05-11 19:04:15 +03:00
ldapsrv_bind_wait_finished ( call , NT_STATUS_OK ) ;
2004-10-08 16:31:13 +04:00
}
2010-09-22 16:24:03 +04:00
struct ldapsrv_sasl_postprocess_context {
2006-07-24 04:45:21 +04:00
struct ldapsrv_connection * conn ;
2010-09-22 16:24:03 +04:00
struct tstream_context * sasl ;
2006-07-24 04:45:21 +04:00
} ;
2010-09-22 16:24:03 +04:00
struct ldapsrv_sasl_postprocess_state {
uint8_t dummy ;
} ;
static struct tevent_req * ldapsrv_sasl_postprocess_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
void * private_data )
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
{
2010-09-22 16:24:03 +04:00
struct ldapsrv_sasl_postprocess_context * context =
talloc_get_type_abort ( private_data ,
struct ldapsrv_sasl_postprocess_context ) ;
struct tevent_req * req ;
struct ldapsrv_sasl_postprocess_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct ldapsrv_sasl_postprocess_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2006-07-25 04:57:27 +04:00
2010-09-22 16:24:03 +04:00
TALLOC_FREE ( context - > conn - > sockets . sasl ) ;
context - > conn - > sockets . sasl = talloc_move ( context - > conn , & context - > sasl ) ;
context - > conn - > sockets . active = context - > conn - > sockets . sasl ;
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
static NTSTATUS ldapsrv_sasl_postprocess_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
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
}
2017-02-20 05:54:47 +03:00
static NTSTATUS ldapsrv_setup_gensec ( struct ldapsrv_connection * conn ,
const char * sasl_mech ,
struct gensec_security * * _gensec_security )
{
NTSTATUS status ;
struct gensec_security * gensec_security ;
status = samba_server_gensec_start ( conn ,
conn - > connection - > event . ctx ,
conn - > connection - > msg_ctx ,
conn - > lp_ctx ,
conn - > server_credentials ,
" ldap " ,
& gensec_security ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2017-03-01 06:49:01 +03:00
status = gensec_set_target_service_description ( gensec_security ,
" LDAP " ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2017-02-21 04:15:05 +03:00
status = gensec_set_remote_address ( gensec_security ,
conn - > connection - > remote_address ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = gensec_set_local_address ( gensec_security ,
conn - > connection - > local_address ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2017-02-20 05:54:47 +03:00
gensec_want_feature ( gensec_security , GENSEC_FEATURE_ASYNC_REPLIES ) ;
gensec_want_feature ( gensec_security , GENSEC_FEATURE_LDAP_STYLE ) ;
2017-03-06 04:10:17 +03:00
if ( conn - > sockets . active = = conn - > sockets . tls ) {
2024-01-23 16:20:24 +03:00
uint32_t initiator_addrtype = 0 ;
const DATA_BLOB * initiator_address = NULL ;
uint32_t acceptor_addrtype = 0 ;
const DATA_BLOB * acceptor_address = NULL ;
const DATA_BLOB * application_data =
tstream_tls_channel_bindings ( conn - > sockets . tls ) ;
status = gensec_set_channel_bindings ( gensec_security ,
initiator_addrtype ,
initiator_address ,
acceptor_addrtype ,
acceptor_address ,
application_data ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/*
* By default channel bindings are required ,
* so we only set GENSEC_FEATURE_CB_OPTIONAL
* for the legacy option :
*
* ldap server require strong auth = no
* or
* ldap server require strong auth =
* allow_sasl_without_tls_channel_bindings
*
* And this as an alias to cope with existing smb . conf
* files :
*
* ldap server require strong auth = allow_sasl_over_tls
*/
switch ( conn - > require_strong_auth ) {
case LDAP_SERVER_REQUIRE_STRONG_AUTH_NO :
case LDAP_SERVER_REQUIRE_STRONG_AUTH_ALLOW_SASL_OVER_TLS :
case LDAP_SERVER_REQUIRE_STRONG_AUTH_ALLOW_SASL_WITHOUT_TLS_CB :
gensec_want_feature ( gensec_security ,
GENSEC_FEATURE_CB_OPTIONAL ) ;
break ;
default :
break ;
}
2017-03-06 04:10:17 +03:00
gensec_want_feature ( gensec_security , GENSEC_FEATURE_LDAPS_TRANSPORT ) ;
}
2017-02-20 05:54:47 +03:00
status = gensec_start_mech_by_sasl_name ( gensec_security , sasl_mech ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
* _gensec_security = gensec_security ;
return status ;
}
2017-05-12 14:15:27 +03:00
static void ldapsrv_BindSASL_done ( struct tevent_req * subreq ) ;
2004-10-10 02:00:00 +04:00
static NTSTATUS ldapsrv_BindSASL ( struct ldapsrv_call * call )
{
2005-06-15 04:27:51 +04:00
struct ldap_BindRequest * req = & call - > request - > r . BindRequest ;
2004-10-10 02:00:00 +04:00
struct ldapsrv_reply * reply ;
struct ldap_BindResponse * resp ;
2005-01-01 01:45:11 +03:00
struct ldapsrv_connection * conn ;
2006-02-04 12:48:22 +03:00
int result = 0 ;
2006-09-09 14:05:58 +04:00
const char * errstr = NULL ;
2004-10-10 02:00:00 +04:00
NTSTATUS status = NT_STATUS_OK ;
2017-05-11 20:13:49 +03:00
DATA_BLOB input = data_blob_null ;
2017-05-12 14:15:27 +03:00
struct tevent_req * subreq = NULL ;
2005-06-14 07:55:27 +04:00
2004-10-10 02:00:00 +04:00
DEBUG ( 10 , ( " BindSASL dn: %s \n " , req - > dn ) ) ;
reply = ldapsrv_init_reply ( call , LDAP_TAG_BindResponse ) ;
if ( ! reply ) {
return NT_STATUS_NO_MEMORY ;
}
2005-06-15 04:27:51 +04:00
resp = & reply - > msg - > r . BindResponse ;
2017-05-11 20:11:43 +03:00
/* Windows 2000 mmc doesn't like secblob == NULL and reports a decoding error */
resp - > SASL . secblob = talloc_zero ( reply , DATA_BLOB ) ;
if ( resp - > SASL . secblob = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2005-01-01 01:45:11 +03:00
conn = call - > conn ;
2004-10-10 02:00:00 +04:00
2006-07-25 11:48:23 +04:00
/*
* TODO : a SASL bind with a different mechanism
* should cancel an inprogress SASL bind .
* ( see RFC 4513 )
*/
2006-02-04 12:48:22 +03:00
if ( ! conn - > gensec ) {
2017-02-20 05:54:47 +03:00
status = ldapsrv_setup_gensec ( conn , req - > creds . SASL . mechanism ,
& conn - > gensec ) ;
2006-02-04 12:48:22 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-02-20 05:54:47 +03:00
DEBUG ( 1 , ( " Failed to start GENSEC server for [%s] code: %s \n " ,
ldb_binary_encode_string ( call , req - > creds . SASL . mechanism ) ,
nt_errstr ( status ) ) ) ;
2006-02-04 12:48:22 +03:00
result = LDAP_OPERATIONS_ERROR ;
errstr = talloc_asprintf ( reply , " SASL: Failed to start authentication system: %s " ,
nt_errstr ( status ) ) ;
2017-05-11 20:04:27 +03:00
goto do_reply ;
2006-02-04 12:48:22 +03:00
}
}
2017-05-11 20:13:49 +03:00
if ( req - > creds . SASL . secblob ) {
input = * req - > creds . SASL . secblob ;
2004-10-10 02:00:00 +04:00
}
2017-05-12 14:15:27 +03:00
subreq = gensec_update_send ( call , conn - > connection - > event . ctx ,
conn - > gensec , input ) ;
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq , ldapsrv_BindSASL_done , call ) ;
status = ldapsrv_bind_wait_setup ( call , reply ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( subreq ) ;
return status ;
}
/*
* The rest will be async .
*/
return NT_STATUS_OK ;
do_reply :
if ( result ! = LDAP_SASL_BIND_IN_PROGRESS ) {
/*
* We should destroy the gensec context
* when we hit a fatal error .
*
* Note : conn - > gensec is already cleared
* for the LDAP_SUCCESS case .
*/
talloc_unlink ( conn , conn - > gensec ) ;
conn - > gensec = NULL ;
}
resp - > response . resultcode = result ;
resp - > response . dn = NULL ;
resp - > response . errormessage = errstr ;
resp - > response . referral = NULL ;
ldapsrv_queue_reply ( call , reply ) ;
return NT_STATUS_OK ;
}
static void ldapsrv_BindSASL_done ( struct tevent_req * subreq )
{
struct ldapsrv_call * call =
tevent_req_callback_data ( subreq ,
struct ldapsrv_call ) ;
struct ldapsrv_bind_wait_context * bind_wait =
talloc_get_type_abort ( call - > wait_private ,
struct ldapsrv_bind_wait_context ) ;
struct ldap_BindRequest * req = & call - > request - > r . BindRequest ;
struct ldapsrv_reply * reply = bind_wait - > reply ;
struct ldap_BindResponse * resp = & reply - > msg - > r . BindResponse ;
struct ldapsrv_connection * conn = call - > conn ;
struct auth_session_info * session_info = NULL ;
struct ldapsrv_sasl_postprocess_context * context = NULL ;
NTSTATUS status ;
int result ;
const char * errstr = NULL ;
2017-09-14 06:07:10 +03:00
char * ldb_errstring = NULL ;
2017-05-12 14:15:27 +03:00
DATA_BLOB output = data_blob_null ;
2020-08-07 14:40:58 +03:00
NTTIME expire_time_nt ;
2017-05-12 14:15:27 +03:00
status = gensec_update_recv ( subreq , call , & output ) ;
TALLOC_FREE ( subreq ) ;
2017-05-11 20:13:49 +03:00
2004-10-10 02:00:00 +04:00
if ( NT_STATUS_EQUAL ( NT_STATUS_MORE_PROCESSING_REQUIRED , status ) ) {
2017-05-11 22:14:00 +03:00
* resp - > SASL . secblob = output ;
2004-10-10 02:00:00 +04:00
result = LDAP_SASL_BIND_IN_PROGRESS ;
errstr = NULL ;
2017-05-11 20:04:27 +03:00
goto do_reply ;
2017-05-11 22:09:08 +03:00
}
2024-01-23 16:20:24 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_BAD_BINDINGS ) ) {
result = LDAP_INVALID_CREDENTIALS ;
errstr = ldapsrv_bind_error_msg ( reply ,
HRES_SEC_E_BAD_BINDINGS ,
0x0C090711 ,
status ) ;
goto do_reply ;
}
2017-05-11 22:09:08 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
status = nt_status_squash ( status ) ;
2017-05-12 17:04:02 +03:00
result = LDAP_INVALID_CREDENTIALS ;
errstr = ldapsrv_bind_error_msg ( reply , HRES_SEC_E_LOGON_DENIED ,
0x0C0904DC , status ) ;
2017-05-11 22:09:08 +03:00
goto do_reply ;
}
2017-05-11 22:11:00 +03:00
if ( gensec_have_feature ( conn - > gensec , GENSEC_FEATURE_SIGN ) | |
gensec_have_feature ( conn - > gensec , GENSEC_FEATURE_SEAL ) ) {
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
2017-05-11 22:18:07 +03:00
context = talloc_zero ( call , struct ldapsrv_sasl_postprocess_context ) ;
2017-05-11 22:17:40 +03:00
if ( context = = NULL ) {
2017-05-12 14:15:27 +03:00
ldapsrv_bind_wait_finished ( call , NT_STATUS_NO_MEMORY ) ;
return ;
2017-05-11 22:11:00 +03:00
}
}
2010-09-22 16:24:03 +04:00
2017-05-11 22:11:00 +03:00
if ( context & & conn - > sockets . tls ) {
TALLOC_FREE ( context ) ;
status = NT_STATUS_NOT_SUPPORTED ;
result = LDAP_UNWILLING_TO_PERFORM ;
errstr = talloc_asprintf ( reply ,
" SASL:[%s]: Sign or Seal are not allowed if TLS is used " ,
req - > creds . SASL . mechanism ) ;
goto do_reply ;
}
2010-09-22 16:24:03 +04:00
2017-05-11 22:11:00 +03:00
if ( context & & conn - > sockets . sasl ) {
TALLOC_FREE ( context ) ;
status = NT_STATUS_NOT_SUPPORTED ;
result = LDAP_UNWILLING_TO_PERFORM ;
errstr = talloc_asprintf ( reply ,
" SASL:[%s]: Sign or Seal are not allowed if SASL encryption has already been set up " ,
req - > creds . SASL . mechanism ) ;
goto do_reply ;
}
2017-05-12 13:04:59 +03:00
if ( context = = NULL ) {
2017-05-11 22:11:00 +03:00
switch ( call - > conn - > require_strong_auth ) {
case LDAP_SERVER_REQUIRE_STRONG_AUTH_NO :
break ;
case LDAP_SERVER_REQUIRE_STRONG_AUTH_ALLOW_SASL_OVER_TLS :
2024-01-23 16:20:24 +03:00
case LDAP_SERVER_REQUIRE_STRONG_AUTH_ALLOW_SASL_WITHOUT_TLS_CB :
case LDAP_SERVER_REQUIRE_STRONG_AUTH_YES :
2017-05-11 22:11:00 +03:00
if ( call - > conn - > sockets . active = = call - > conn - > sockets . tls ) {
break ;
}
status = NT_STATUS_NETWORK_ACCESS_DENIED ;
result = LDAP_STRONG_AUTH_REQUIRED ;
2011-01-06 07:25:29 +03:00
errstr = talloc_asprintf ( reply ,
2017-05-11 22:11:00 +03:00
" SASL:[%s]: Sign or Seal are required. " ,
req - > creds . SASL . mechanism ) ;
2017-05-11 20:04:27 +03:00
goto do_reply ;
2011-01-06 07:25:29 +03:00
}
2017-05-11 22:11:00 +03:00
}
2011-01-06 07:25:29 +03:00
2017-05-12 13:04:59 +03:00
if ( context ! = NULL ) {
context - > conn = conn ;
status = gensec_create_tstream ( context ,
context - > conn - > gensec ,
context - > conn - > sockets . raw ,
& context - > sasl ) ;
2017-05-12 13:26:12 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
result = LDAP_OPERATIONS_ERROR ;
errstr = talloc_asprintf ( reply ,
2017-05-11 22:11:00 +03:00
" SASL:[%s]: Failed to setup SASL socket: %s " ,
req - > creds . SASL . mechanism , nt_errstr ( status ) ) ;
2017-05-12 13:26:12 +03:00
goto do_reply ;
}
}
2017-05-12 13:27:26 +03:00
status = gensec_session_info ( conn - > gensec , call , & session_info ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
result = LDAP_OPERATIONS_ERROR ;
errstr = talloc_asprintf ( reply ,
" SASL:[%s]: Failed to get session info: %s " ,
req - > creds . SASL . mechanism , nt_errstr ( status ) ) ;
goto do_reply ;
2017-05-12 13:27:26 +03:00
}
2017-05-11 22:11:00 +03:00
2017-05-12 13:27:26 +03:00
talloc_unlink ( conn , conn - > session_info ) ;
conn - > session_info = talloc_steal ( conn , session_info ) ;
2017-05-11 22:11:00 +03:00
2017-05-12 13:27:26 +03:00
/* don't leak the old LDB */
talloc_unlink ( conn , conn - > ldb ) ;
2017-05-11 22:11:00 +03:00
2017-05-12 13:27:26 +03:00
call - > conn - > authz_logged = true ;
2006-01-13 03:38:35 +03:00
2017-09-14 06:07:10 +03:00
result = ldapsrv_backend_Init ( call - > conn , & ldb_errstring ) ;
2017-05-12 13:27:26 +03:00
2017-09-14 06:07:10 +03:00
if ( result ! = LDB_SUCCESS ) {
/* Only put the detailed error in DEBUG() */
2023-08-07 07:52:59 +03:00
DBG_ERR ( " ldapsrv_backend_Init failed: %s: %s \n " ,
2017-09-14 06:07:10 +03:00
ldb_errstring , ldb_strerror ( result ) ) ;
errstr = talloc_strdup ( reply ,
" SASL Bind: Failed to advise "
" ldb new credentials " ) ;
result = LDB_ERR_OPERATIONS_ERROR ;
2019-05-08 06:52:31 +03:00
goto do_reply ;
2017-05-11 22:11:00 +03:00
}
2010-09-22 16:24:03 +04:00
2020-08-07 14:40:58 +03:00
expire_time_nt = gensec_expire_time ( conn - > gensec ) ;
if ( expire_time_nt ! = GENSEC_EXPIRE_TIME_INFINITY ) {
struct timeval_buf buf ;
nttime_to_timeval ( & conn - > limits . expire_time , expire_time_nt ) ;
DBG_DEBUG ( " Setting connection expire_time to %s \n " ,
timeval_str_buf ( & conn - > limits . expire_time ,
false ,
true ,
& buf ) ) ;
}
2017-05-12 13:31:25 +03:00
if ( context ! = NULL ) {
2017-05-12 13:38:59 +03:00
const void * ptr = NULL ;
ptr = talloc_reparent ( conn , context - > sasl , conn - > gensec ) ;
if ( ptr = = NULL ) {
2017-05-12 14:15:27 +03:00
ldapsrv_bind_wait_finished ( call , NT_STATUS_NO_MEMORY ) ;
return ;
2017-05-12 13:38:59 +03:00
}
2017-05-11 22:11:00 +03:00
call - > postprocess_send = ldapsrv_sasl_postprocess_send ;
call - > postprocess_recv = ldapsrv_sasl_postprocess_recv ;
call - > postprocess_private = context ;
2017-05-12 13:38:59 +03:00
} else {
talloc_unlink ( conn , conn - > gensec ) ;
2004-10-10 02:00:00 +04:00
}
2017-05-11 22:11:00 +03:00
conn - > gensec = NULL ;
2004-10-10 02:00:00 +04:00
2017-05-11 22:14:00 +03:00
* resp - > SASL . secblob = output ;
2017-05-12 13:41:13 +03:00
result = LDAP_SUCCESS ;
errstr = NULL ;
2017-05-11 22:14:00 +03:00
2017-05-11 20:04:27 +03:00
do_reply :
2017-05-12 13:44:05 +03:00
if ( result ! = LDAP_SASL_BIND_IN_PROGRESS ) {
/*
* We should destroy the gensec context
* when we hit a fatal error .
*
* Note : conn - > gensec is already cleared
* for the LDAP_SUCCESS case .
*/
talloc_unlink ( conn , conn - > gensec ) ;
conn - > gensec = NULL ;
}
2004-10-10 02:00:00 +04:00
resp - > response . resultcode = result ;
resp - > response . dn = NULL ;
resp - > response . errormessage = errstr ;
resp - > response . referral = NULL ;
2005-06-19 13:31:34 +04:00
ldapsrv_queue_reply ( call , reply ) ;
2017-05-12 14:15:27 +03:00
ldapsrv_bind_wait_finished ( call , NT_STATUS_OK ) ;
2004-10-10 02:00:00 +04:00
}
NTSTATUS ldapsrv_BindRequest ( struct ldapsrv_call * call )
{
2005-06-15 04:27:51 +04:00
struct ldap_BindRequest * req = & call - > request - > r . BindRequest ;
2004-10-10 02:00:00 +04:00
struct ldapsrv_reply * reply ;
struct ldap_BindResponse * resp ;
2015-07-23 13:08:42 +03:00
if ( call - > conn - > pending_calls ! = NULL ) {
reply = ldapsrv_init_reply ( call , LDAP_TAG_BindResponse ) ;
if ( ! reply ) {
return NT_STATUS_NO_MEMORY ;
}
resp = & reply - > msg - > r . BindResponse ;
resp - > response . resultcode = LDAP_BUSY ;
resp - > response . dn = NULL ;
resp - > response . errormessage = talloc_asprintf ( reply , " Pending requests on this LDAP session " ) ;
resp - > response . referral = NULL ;
resp - > SASL . secblob = NULL ;
ldapsrv_queue_reply ( call , reply ) ;
return NT_STATUS_OK ;
}
2006-07-25 11:48:23 +04:00
/*
2015-07-23 13:08:42 +03:00
* TODO : a simple bind should cancel an
2006-07-25 11:48:23 +04:00
* inprogress SASL bind .
* ( see RFC 4513 )
*/
2004-10-10 02:00:00 +04:00
switch ( req - > mechanism ) {
case LDAP_AUTH_MECH_SIMPLE :
return ldapsrv_BindSimple ( call ) ;
case LDAP_AUTH_MECH_SASL :
return ldapsrv_BindSASL ( call ) ;
}
reply = ldapsrv_init_reply ( call , LDAP_TAG_BindResponse ) ;
if ( ! reply ) {
return NT_STATUS_NO_MEMORY ;
}
2005-06-15 04:27:51 +04:00
resp = & reply - > msg - > r . BindResponse ;
2015-07-23 13:17:02 +03:00
resp - > response . resultcode = LDAP_AUTH_METHOD_NOT_SUPPORTED ;
2004-10-10 02:00:00 +04:00
resp - > response . dn = NULL ;
resp - > response . errormessage = talloc_asprintf ( reply , " Bad AuthenticationChoice [%d] " , req - > mechanism ) ;
resp - > response . referral = NULL ;
2006-02-15 18:19:10 +03:00
resp - > SASL . secblob = NULL ;
2004-10-10 02:00:00 +04:00
2005-06-19 13:31:34 +04:00
ldapsrv_queue_reply ( call , reply ) ;
return NT_STATUS_OK ;
2004-10-10 02:00:00 +04:00
}
2017-06-13 16:02:41 +03:00
struct ldapsrv_unbind_wait_context {
uint8_t dummy ;
} ;
struct ldapsrv_unbind_wait_state {
uint8_t dummy ;
} ;
static struct tevent_req * ldapsrv_unbind_wait_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
void * private_data )
{
struct ldapsrv_unbind_wait_context * unbind_wait =
talloc_get_type_abort ( private_data ,
struct ldapsrv_unbind_wait_context ) ;
struct tevent_req * req ;
struct ldapsrv_unbind_wait_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct ldapsrv_unbind_wait_state ) ;
if ( req = = NULL ) {
return NULL ;
}
( void ) unbind_wait ;
tevent_req_nterror ( req , NT_STATUS_LOCAL_DISCONNECT ) ;
return tevent_req_post ( req , ev ) ;
}
static NTSTATUS ldapsrv_unbind_wait_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
}
static NTSTATUS ldapsrv_unbind_wait_setup ( struct ldapsrv_call * call )
{
struct ldapsrv_unbind_wait_context * unbind_wait = NULL ;
if ( call - > wait_private ! = NULL ) {
return NT_STATUS_INTERNAL_ERROR ;
}
unbind_wait = talloc_zero ( call , struct ldapsrv_unbind_wait_context ) ;
if ( unbind_wait = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
call - > wait_private = unbind_wait ;
call - > wait_send = ldapsrv_unbind_wait_send ;
call - > wait_recv = ldapsrv_unbind_wait_recv ;
return NT_STATUS_OK ;
}
2004-10-08 16:31:13 +04:00
NTSTATUS ldapsrv_UnbindRequest ( struct ldapsrv_call * call )
{
2017-06-13 16:02:41 +03:00
struct ldapsrv_call * c = NULL ;
struct ldapsrv_call * n = NULL ;
2004-10-08 16:31:13 +04:00
DEBUG ( 10 , ( " UnbindRequest \n " ) ) ;
2017-06-13 16:02:41 +03:00
for ( c = call - > conn - > pending_calls ; c ! = NULL ; c = n ) {
n = c - > next ;
DLIST_REMOVE ( call - > conn - > pending_calls , c ) ;
TALLOC_FREE ( c ) ;
}
return ldapsrv_unbind_wait_setup ( call ) ;
2004-10-08 16:31:13 +04:00
}