2005-06-16 09:39:40 +04:00
/*
Unix SMB / CIFS mplementation .
LDAP bind calls
Copyright ( C ) Andrew Tridgell 2005
Copyright ( C ) Volker Lendecke 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-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"
# include "auth/auth.h"
static struct ldap_message * new_ldap_simple_bind_msg ( struct ldap_connection * conn ,
const char * dn , const char * pw )
{
struct ldap_message * res ;
res = new_ldap_message ( conn ) ;
if ( ! res ) {
return NULL ;
}
res - > type = LDAP_TAG_BindRequest ;
res - > r . BindRequest . version = 3 ;
res - > r . BindRequest . dn = talloc_strdup ( res , dn ) ;
res - > r . BindRequest . mechanism = LDAP_AUTH_MECH_SIMPLE ;
res - > r . BindRequest . creds . password = talloc_strdup ( res , pw ) ;
2006-01-06 07:01:23 +03:00
res - > controls = NULL ;
2005-06-16 09:39:40 +04:00
return res ;
}
/*
perform a simple username / password bind
*/
NTSTATUS ldap_bind_simple ( struct ldap_connection * conn ,
const char * userdn , const char * password )
{
struct ldap_request * req ;
struct ldap_message * msg ;
const char * dn , * pw ;
NTSTATUS status ;
if ( conn = = NULL ) {
return NT_STATUS_INVALID_CONNECTION ;
}
if ( userdn ) {
dn = userdn ;
} else {
if ( conn - > auth_dn ) {
dn = conn - > auth_dn ;
} else {
dn = " " ;
}
}
if ( password ) {
pw = password ;
} else {
if ( conn - > simple_pw ) {
pw = conn - > simple_pw ;
} else {
pw = " " ;
}
}
msg = new_ldap_simple_bind_msg ( conn , dn , pw ) ;
NT_STATUS_HAVE_NO_MEMORY ( msg ) ;
/* send the request */
req = ldap_request_send ( conn , msg ) ;
talloc_free ( msg ) ;
NT_STATUS_HAVE_NO_MEMORY ( req ) ;
/* wait for replies */
status = ldap_request_wait ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( req ) ;
return status ;
}
/* check its a valid reply */
msg = req - > replies [ 0 ] ;
if ( msg - > type ! = LDAP_TAG_BindResponse ) {
talloc_free ( req ) ;
return NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
}
status = ldap_check_response ( conn , & msg - > r . BindResponse . response ) ;
talloc_free ( req ) ;
return status ;
}
static struct ldap_message * new_ldap_sasl_bind_msg ( struct ldap_connection * conn ,
const char * sasl_mechanism ,
DATA_BLOB * secblob )
{
struct ldap_message * res ;
res = new_ldap_message ( conn ) ;
if ( ! res ) {
return NULL ;
}
res - > type = LDAP_TAG_BindRequest ;
res - > r . BindRequest . version = 3 ;
res - > r . BindRequest . dn = " " ;
res - > r . BindRequest . mechanism = LDAP_AUTH_MECH_SASL ;
res - > r . BindRequest . creds . SASL . mechanism = talloc_strdup ( res , sasl_mechanism ) ;
res - > r . BindRequest . creds . SASL . secblob = * secblob ;
2006-01-06 07:01:23 +03:00
res - > controls = NULL ;
2005-06-16 09:39:40 +04:00
return res ;
}
/*
perform a sasl bind using the given credentials
*/
NTSTATUS ldap_bind_sasl ( struct ldap_connection * conn , struct cli_credentials * creds )
{
NTSTATUS status ;
TALLOC_CTX * tmp_ctx = NULL ;
2005-08-23 09:29:37 +04:00
2005-06-16 09:39:40 +04:00
DATA_BLOB input = data_blob ( NULL , 0 ) ;
DATA_BLOB output = data_blob ( NULL , 0 ) ;
2005-11-05 14:02:37 +03:00
struct ldap_message * * sasl_mechs_msgs ;
struct ldap_SearchResEntry * search ;
int count , i ;
const char * * sasl_names ;
static const char * supported_sasl_mech_attrs [ ] = {
" supportedSASLMechanisms " ,
NULL
} ;
2005-06-16 15:36:09 +04:00
status = gensec_client_start ( conn , & conn - > gensec , NULL ) ;
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Failed to start GENSEC engine (%s) \n " , nt_errstr ( status ) ) ) ;
goto failed ;
}
gensec_want_feature ( conn - > gensec , 0 | GENSEC_FEATURE_SIGN | GENSEC_FEATURE_SEAL ) ;
status = gensec_set_credentials ( conn - > gensec , creds ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-06-18 13:09:27 +04:00
DEBUG ( 1 , ( " Failed to set GENSEC creds: %s \n " ,
2005-06-16 09:39:40 +04:00
nt_errstr ( status ) ) ) ;
goto failed ;
}
status = gensec_set_target_hostname ( conn - > gensec , conn - > host ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-06-18 13:09:27 +04:00
DEBUG ( 1 , ( " Failed to set GENSEC target hostname: %s \n " ,
2005-06-16 09:39:40 +04:00
nt_errstr ( status ) ) ) ;
goto failed ;
}
status = gensec_set_target_service ( conn - > gensec , " ldap " ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-06-18 13:09:27 +04:00
DEBUG ( 1 , ( " Failed to set GENSEC target service: %s \n " ,
2005-06-16 09:39:40 +04:00
nt_errstr ( status ) ) ) ;
goto failed ;
}
2005-11-05 14:02:37 +03:00
status = ildap_search ( conn , " " , LDAP_SEARCH_SCOPE_BASE , " " , supported_sasl_mech_attrs ,
2006-01-06 07:01:23 +03:00
False , NULL , NULL , & sasl_mechs_msgs ) ;
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-11-05 14:02:37 +03:00
DEBUG ( 1 , ( " Failed to inquire of target's available sasl mechs in rootdse search: %s \n " ,
2005-06-16 09:39:40 +04:00
nt_errstr ( status ) ) ) ;
goto failed ;
}
2005-11-05 14:02:37 +03:00
count = ildap_count_entries ( conn , sasl_mechs_msgs ) ;
if ( count ! = 1 ) {
DEBUG ( 1 , ( " Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d \n " ,
count ) ) ;
goto failed ;
}
2005-06-16 09:39:40 +04:00
tmp_ctx = talloc_new ( conn ) ;
if ( tmp_ctx = = NULL ) goto failed ;
2005-11-05 14:02:37 +03:00
search = & sasl_mechs_msgs [ 0 ] - > r . SearchResultEntry ;
if ( search - > num_attributes ! = 1 ) {
DEBUG ( 1 , ( " Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d \n " ,
search - > num_attributes ) ) ;
goto failed ;
}
sasl_names = talloc_array ( tmp_ctx , const char * , search - > attributes [ 0 ] . num_values + 1 ) ;
if ( ! sasl_names ) {
DEBUG ( 1 , ( " talloc_arry(char *, %d) failed \n " ,
count ) ) ;
goto failed ;
}
for ( i = 0 ; i < search - > attributes [ 0 ] . num_values ; i + + ) {
sasl_names [ i ] = ( const char * ) search - > attributes [ 0 ] . values [ i ] . data ;
}
sasl_names [ i ] = NULL ;
2006-02-04 12:53:50 +03:00
status = gensec_start_mech_by_sasl_list ( conn - > gensec , sasl_names ) ;
2005-11-05 14:02:37 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2006-02-04 12:53:50 +03:00
DEBUG ( 1 , ( " None of the %d proposed SASL mechs were acceptable: %s \n " ,
count , nt_errstr ( status ) ) ) ;
2005-11-05 14:02:37 +03:00
goto failed ;
}
2005-06-16 09:39:40 +04:00
while ( 1 ) {
2005-08-23 09:29:37 +04:00
NTSTATUS gensec_status ;
2005-06-16 09:39:40 +04:00
struct ldap_message * response ;
struct ldap_message * msg ;
struct ldap_request * req ;
int result = LDAP_OTHER ;
2005-08-23 09:29:37 +04:00
status = gensec_update ( conn - > gensec , tmp_ctx ,
input ,
& output ) ;
/* The status value here, from GENSEC is vital to the security
* of the system . Even if the other end accepts , if GENSEC
* claims ' MORE_PROCESSING_REQUIRED ' then you must keep
* feeding it blobs , or else the remote host / attacker might
* avoid mutal authentication requirements .
*
* Likewise , you must not feed GENSEC too much ( after the OK ) ,
* it doesn ' t like that either
*/
gensec_status = status ;
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) & &
! NT_STATUS_IS_OK ( status ) ) {
break ;
}
2006-02-04 12:53:50 +03:00
if ( NT_STATUS_IS_OK ( status ) & & output . length = = 0 ) {
2005-08-23 09:29:37 +04:00
break ;
}
2005-06-16 09:39:40 +04:00
2006-02-04 12:53:50 +03:00
/* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
msg = new_ldap_sasl_bind_msg ( tmp_ctx , conn - > gensec - > ops - > sasl_name , & output ) ;
2005-06-16 09:39:40 +04:00
if ( msg = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto failed ;
}
req = ldap_request_send ( conn , msg ) ;
if ( req = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto failed ;
}
talloc_steal ( tmp_ctx , req ) ;
status = ldap_result_n ( req , 0 , & response ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto failed ;
}
if ( response - > type ! = LDAP_TAG_BindResponse ) {
status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
goto failed ;
}
result = response - > r . BindResponse . response . resultcode ;
if ( result ! = LDAP_SUCCESS & & result ! = LDAP_SASL_BIND_IN_PROGRESS ) {
2005-10-17 15:50:34 +04:00
status = ldap_check_response ( conn ,
& response - > r . BindResponse . response ) ;
2005-06-16 09:39:40 +04:00
break ;
}
2005-08-23 09:29:37 +04:00
/* This is where we check if GENSEC wanted to be fed more data */
if ( ! NT_STATUS_EQUAL ( gensec_status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
break ;
}
input = response - > r . BindResponse . SASL . secblob ;
2005-06-16 09:39:40 +04:00
}
if ( NT_STATUS_IS_OK ( status ) & &
2005-06-24 04:03:47 +04:00
( gensec_have_feature ( conn - > gensec , GENSEC_FEATURE_SEAL ) | |
2005-06-16 09:39:40 +04:00
gensec_have_feature ( conn - > gensec , GENSEC_FEATURE_SIGN ) ) ) {
conn - > enable_wrap = True ;
}
talloc_free ( tmp_ctx ) ;
return status ;
failed :
talloc_free ( tmp_ctx ) ;
talloc_free ( conn - > gensec ) ;
conn - > gensec = NULL ;
return status ;
}