2001-12-08 14:18:56 +03:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2001-12-08 14:18:56 +03:00
ads sasl code
Copyright ( C ) Andrew Tridgell 2001
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-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2001-12-08 14:18:56 +03: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 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2001-12-08 14:18:56 +03:00
*/
# include "includes.h"
2009-09-17 02:21:01 +04:00
# include "../libcli/auth/spnego.h"
2011-12-27 05:27:11 +04:00
# include "auth/gensec/gensec.h"
# include "auth_generic.h"
2010-07-02 02:32:52 +04:00
# include "ads.h"
# include "smb_krb5.h"
2012-04-24 20:37:13 +04:00
# include "system/gssapi.h"
2001-12-08 14:18:56 +03:00
2002-10-01 22:26:00 +04:00
# ifdef HAVE_LDAP
2001-12-08 14:18:56 +03:00
2007-07-18 12:15:42 +04:00
static ADS_STATUS ads_sasl_ntlmssp_wrap ( ADS_STRUCT * ads , uint8 * buf , uint32 len )
{
2011-12-27 05:27:11 +04:00
struct gensec_security * gensec_security =
talloc_get_type_abort ( ads - > ldap . wrap_private_data ,
struct gensec_security ) ;
2007-07-18 12:15:42 +04:00
NTSTATUS nt_status ;
2011-12-27 05:27:11 +04:00
DATA_BLOB unwrapped , wrapped ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
unwrapped = data_blob_const ( buf , len ) ;
nt_status = gensec_wrap ( gensec_security , frame , & unwrapped , & wrapped ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
TALLOC_FREE ( frame ) ;
return ADS_ERROR_NT ( nt_status ) ;
2007-07-18 12:15:42 +04:00
}
2011-12-27 05:27:11 +04:00
if ( ( ads - > ldap . out . size - 4 ) < wrapped . length ) {
return ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
}
2007-07-18 12:15:42 +04:00
2011-12-27 05:27:11 +04:00
/* copy the wrapped blob to the right location */
memcpy ( ads - > ldap . out . buf + 4 , wrapped . data , wrapped . length ) ;
2007-07-18 12:15:42 +04:00
/* set how many bytes must be written to the underlying socket */
2011-12-27 05:27:11 +04:00
ads - > ldap . out . left = 4 + wrapped . length ;
TALLOC_FREE ( frame ) ;
2007-07-18 12:15:42 +04:00
return ADS_SUCCESS ;
}
static ADS_STATUS ads_sasl_ntlmssp_unwrap ( ADS_STRUCT * ads )
{
2011-12-27 05:27:11 +04:00
struct gensec_security * gensec_security =
talloc_get_type_abort ( ads - > ldap . wrap_private_data ,
struct gensec_security ) ;
2007-07-18 12:15:42 +04:00
NTSTATUS nt_status ;
2011-12-27 05:27:11 +04:00
DATA_BLOB unwrapped , wrapped ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
wrapped = data_blob_const ( ads - > ldap . in . buf + 4 , ads - > ldap . in . ofs - 4 ) ;
nt_status = gensec_unwrap ( gensec_security , frame , & wrapped , & unwrapped ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
TALLOC_FREE ( frame ) ;
return ADS_ERROR_NT ( nt_status ) ;
}
if ( wrapped . length < unwrapped . length ) {
TALLOC_FREE ( frame ) ;
return ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
2007-07-18 12:15:42 +04:00
}
2011-12-27 05:27:11 +04:00
/* copy the wrapped blob to the right location */
memcpy ( ads - > ldap . in . buf + 4 , unwrapped . data , unwrapped . length ) ;
/* set how many bytes must be written to the underlying socket */
ads - > ldap . in . left = unwrapped . length ;
ads - > ldap . in . ofs = 4 ;
TALLOC_FREE ( frame ) ;
2007-07-18 12:15:42 +04:00
return ADS_SUCCESS ;
}
static void ads_sasl_ntlmssp_disconnect ( ADS_STRUCT * ads )
{
2011-12-27 05:27:11 +04:00
struct gensec_security * gensec_security =
talloc_get_type_abort ( ads - > ldap . wrap_private_data ,
struct gensec_security ) ;
2007-07-18 12:15:42 +04:00
2011-12-27 05:27:11 +04:00
TALLOC_FREE ( gensec_security ) ;
2007-07-18 12:15:42 +04:00
ads - > ldap . wrap_ops = NULL ;
ads - > ldap . wrap_private_data = NULL ;
}
static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
. name = " ntlmssp " ,
. wrap = ads_sasl_ntlmssp_wrap ,
. unwrap = ads_sasl_ntlmssp_unwrap ,
. disconnect = ads_sasl_ntlmssp_disconnect
} ;
2002-09-25 19:19:00 +04:00
/*
perform a LDAP / SASL / SPNEGO / NTLMSSP bind ( just how many layers can
we fit on one socket ? ? )
2001-12-08 14:18:56 +03:00
*/
2002-09-25 19:19:00 +04:00
static ADS_STATUS ads_sasl_spnego_ntlmssp_bind ( ADS_STRUCT * ads )
2001-12-08 14:18:56 +03:00
{
2007-05-14 16:16:20 +04:00
DATA_BLOB msg1 = data_blob_null ;
DATA_BLOB blob = data_blob_null ;
DATA_BLOB blob_in = data_blob_null ;
DATA_BLOB blob_out = data_blob_null ;
2004-05-07 03:08:56 +04:00
struct berval cred , * scred = NULL ;
2002-09-25 19:19:00 +04:00
int rc ;
2005-11-04 20:39:42 +03:00
NTSTATUS nt_status ;
2007-07-31 16:30:37 +04:00
ADS_STATUS status ;
2005-11-04 20:39:42 +03:00
int turn = 1 ;
2001-12-08 14:18:56 +03:00
2011-12-27 05:27:11 +04:00
struct auth_generic_state * auth_generic_state ;
2002-09-25 19:19:00 +04:00
2011-12-27 05:27:11 +04:00
nt_status = auth_generic_client_prepare ( NULL , & auth_generic_state ) ;
2009-12-30 16:13:45 +03:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2005-11-04 20:39:42 +03:00
return ADS_ERROR_NT ( nt_status ) ;
2002-09-25 19:19:00 +04:00
}
2011-12-27 05:27:11 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status = auth_generic_set_username ( auth_generic_state , ads - > auth . user_name ) ) ) {
2005-11-04 20:39:42 +03:00
return ADS_ERROR_NT ( nt_status ) ;
}
2011-12-27 05:27:11 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status = auth_generic_set_domain ( auth_generic_state , ads - > auth . realm ) ) ) {
2005-11-04 20:39:42 +03:00
return ADS_ERROR_NT ( nt_status ) ;
}
2011-12-27 05:27:11 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status = auth_generic_set_password ( auth_generic_state , ads - > auth . password ) ) ) {
2005-11-04 20:39:42 +03:00
return ADS_ERROR_NT ( nt_status ) ;
2002-09-25 19:19:00 +04:00
}
2007-07-18 12:15:42 +04:00
switch ( ads - > ldap . wrap_type ) {
case ADS_SASLWRAP_TYPE_SEAL :
2011-12-27 05:27:11 +04:00
gensec_want_feature ( auth_generic_state - > gensec_security , GENSEC_FEATURE_SIGN ) ;
gensec_want_feature ( auth_generic_state - > gensec_security , GENSEC_FEATURE_SEAL ) ;
2007-07-18 12:15:42 +04:00
break ;
case ADS_SASLWRAP_TYPE_SIGN :
if ( ads - > auth . flags & ADS_AUTH_SASL_FORCE ) {
2011-12-27 05:27:11 +04:00
gensec_want_feature ( auth_generic_state - > gensec_security , GENSEC_FEATURE_SIGN ) ;
2007-07-18 12:15:42 +04:00
} else {
/*
* windows servers are broken with sign only ,
* so we need to use seal here too
*/
2011-12-27 05:27:11 +04:00
gensec_want_feature ( auth_generic_state - > gensec_security , GENSEC_FEATURE_SIGN ) ;
gensec_want_feature ( auth_generic_state - > gensec_security , GENSEC_FEATURE_SEAL ) ;
2007-07-18 12:15:42 +04:00
ads - > ldap . wrap_type = ADS_SASLWRAP_TYPE_SEAL ;
}
break ;
case ADS_SASLWRAP_TYPE_PLAIN :
break ;
}
2011-12-27 05:27:11 +04:00
nt_status = auth_generic_client_start ( auth_generic_state , GENSEC_OID_NTLMSSP ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return ADS_ERROR_NT ( nt_status ) ;
}
2007-07-18 12:15:42 +04:00
2007-05-14 16:16:20 +04:00
blob_in = data_blob_null ;
2005-11-04 20:39:42 +03:00
do {
2011-12-27 05:27:11 +04:00
nt_status = gensec_update ( auth_generic_state - > gensec_security ,
talloc_tos ( ) , NULL , blob_in , & blob_out ) ;
2005-11-04 20:39:42 +03:00
data_blob_free ( & blob_in ) ;
if ( ( NT_STATUS_EQUAL ( nt_status , NT_STATUS_MORE_PROCESSING_REQUIRED )
| | NT_STATUS_IS_OK ( nt_status ) )
& & blob_out . length ) {
if ( turn = = 1 ) {
2010-07-20 04:14:26 +04:00
const char * OIDs_ntlm [ ] = { OID_NTLMSSP , NULL } ;
2005-11-04 20:39:42 +03:00
/* and wrap it in a SPNEGO wrapper */
2010-07-21 01:59:31 +04:00
msg1 = spnego_gen_negTokenInit ( talloc_tos ( ) ,
OIDs_ntlm , & blob_out , NULL ) ;
2005-11-04 20:39:42 +03:00
} else {
/* wrap it in SPNEGO */
2010-07-21 03:17:58 +04:00
msg1 = spnego_gen_auth ( talloc_tos ( ) , blob_out ) ;
2005-11-04 20:39:42 +03:00
}
data_blob_free ( & blob_out ) ;
cred . bv_val = ( char * ) msg1 . data ;
cred . bv_len = msg1 . length ;
scred = NULL ;
2007-07-16 15:08:00 +04:00
rc = ldap_sasl_bind_s ( ads - > ldap . ld , NULL , " GSS-SPNEGO " , & cred , NULL , NULL , & scred ) ;
2005-11-04 20:39:42 +03:00
data_blob_free ( & msg1 ) ;
if ( ( rc ! = LDAP_SASL_BIND_IN_PROGRESS ) & & ( rc ! = 0 ) ) {
if ( scred ) {
ber_bvfree ( scred ) ;
}
2011-12-27 05:27:11 +04:00
TALLOC_FREE ( auth_generic_state ) ;
2005-11-04 20:39:42 +03:00
return ADS_ERROR ( rc ) ;
}
if ( scred ) {
blob = data_blob ( scred - > bv_val , scred - > bv_len ) ;
ber_bvfree ( scred ) ;
} else {
2007-05-14 16:16:20 +04:00
blob = data_blob_null ;
2005-11-04 20:39:42 +03:00
}
} else {
2011-12-27 05:27:11 +04:00
TALLOC_FREE ( auth_generic_state ) ;
2005-11-04 20:39:42 +03:00
data_blob_free ( & blob_out ) ;
return ADS_ERROR_NT ( nt_status ) ;
}
if ( ( turn = = 1 ) & &
( rc = = LDAP_SASL_BIND_IN_PROGRESS ) ) {
2007-05-14 16:16:20 +04:00
DATA_BLOB tmp_blob = data_blob_null ;
2005-11-04 20:39:42 +03:00
/* the server might give us back two challenges */
2010-07-21 03:17:58 +04:00
if ( ! spnego_parse_challenge ( talloc_tos ( ) , blob , & blob_in ,
2005-11-04 20:39:42 +03:00
& tmp_blob ) ) {
2011-12-27 05:27:11 +04:00
TALLOC_FREE ( auth_generic_state ) ;
2005-11-04 20:39:42 +03:00
data_blob_free ( & blob ) ;
DEBUG ( 3 , ( " Failed to parse challenges \n " ) ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_PARAMETER ) ;
}
data_blob_free ( & tmp_blob ) ;
} else if ( rc = = LDAP_SASL_BIND_IN_PROGRESS ) {
2010-07-21 03:17:58 +04:00
if ( ! spnego_parse_auth_response ( talloc_tos ( ) , blob , nt_status , OID_NTLMSSP ,
2005-11-04 20:39:42 +03:00
& blob_in ) ) {
2011-12-27 05:27:11 +04:00
TALLOC_FREE ( auth_generic_state ) ;
2005-11-04 20:39:42 +03:00
data_blob_free ( & blob ) ;
DEBUG ( 3 , ( " Failed to parse auth response \n " ) ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_PARAMETER ) ;
}
}
data_blob_free ( & blob ) ;
data_blob_free ( & blob_out ) ;
turn + + ;
} while ( rc = = LDAP_SASL_BIND_IN_PROGRESS & & ! NT_STATUS_IS_OK ( nt_status ) ) ;
2004-05-07 03:08:56 +04:00
2007-07-18 12:15:42 +04:00
if ( ads - > ldap . wrap_type > ADS_SASLWRAP_TYPE_PLAIN ) {
2011-12-27 05:27:11 +04:00
uint32_t sig_size = gensec_sig_size ( auth_generic_state - > gensec_security , 0 ) ;
ads - > ldap . out . max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - sig_size ;
ads - > ldap . out . sig_size = sig_size ;
2007-08-02 21:41:47 +04:00
ads - > ldap . in . min_wrapped = ads - > ldap . out . sig_size ;
ads - > ldap . in . max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED ;
2011-12-27 05:27:11 +04:00
status = ads_setup_sasl_wrapping ( ads , & ads_sasl_ntlmssp_ops , auth_generic_state - > gensec_security ) ;
2007-07-31 16:27:25 +04:00
if ( ! ADS_ERR_OK ( status ) ) {
2007-07-31 16:30:37 +04:00
DEBUG ( 0 , ( " ads_setup_sasl_wrapping() failed: %s \n " ,
2007-07-31 16:27:25 +04:00
ads_errstr ( status ) ) ) ;
2011-12-27 05:27:11 +04:00
TALLOC_FREE ( auth_generic_state ) ;
2007-07-31 16:27:25 +04:00
return status ;
}
2011-12-27 05:27:11 +04:00
/* Only keep the gensec_security element around long-term */
talloc_steal ( NULL , auth_generic_state - > gensec_security ) ;
2007-07-18 12:15:42 +04:00
}
2011-12-27 05:27:11 +04:00
TALLOC_FREE ( auth_generic_state ) ;
2004-05-07 03:08:56 +04:00
2005-11-04 20:39:42 +03:00
return ADS_ERROR ( rc ) ;
2001-12-08 14:18:56 +03:00
}
2002-09-25 19:19:00 +04:00
2012-02-13 04:23:15 +04:00
# ifdef HAVE_KRB5
2007-07-18 12:19:13 +04:00
static ADS_STATUS ads_sasl_gssapi_wrap ( ADS_STRUCT * ads , uint8 * buf , uint32 len )
{
2007-09-29 10:44:39 +04:00
gss_ctx_id_t context_handle = ( gss_ctx_id_t ) ads - > ldap . wrap_private_data ;
2007-07-18 12:19:13 +04:00
ADS_STATUS status ;
int gss_rc ;
uint32 minor_status ;
gss_buffer_desc unwrapped , wrapped ;
int conf_req_flag , conf_state ;
unwrapped . value = buf ;
unwrapped . length = len ;
/* for now request sign and seal */
conf_req_flag = ( ads - > ldap . wrap_type = = ADS_SASLWRAP_TYPE_SEAL ) ;
gss_rc = gss_wrap ( & minor_status , context_handle ,
conf_req_flag , GSS_C_QOP_DEFAULT ,
& unwrapped , & conf_state ,
& wrapped ) ;
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
if ( ! ADS_ERR_OK ( status ) ) return status ;
if ( conf_req_flag & & conf_state = = 0 ) {
return ADS_ERROR_NT ( NT_STATUS_ACCESS_DENIED ) ;
}
if ( ( ads - > ldap . out . size - 4 ) < wrapped . length ) {
return ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
}
/* copy the wrapped blob to the right location */
memcpy ( ads - > ldap . out . buf + 4 , wrapped . value , wrapped . length ) ;
/* set how many bytes must be written to the underlying socket */
ads - > ldap . out . left = 4 + wrapped . length ;
gss_release_buffer ( & minor_status , & wrapped ) ;
return ADS_SUCCESS ;
}
static ADS_STATUS ads_sasl_gssapi_unwrap ( ADS_STRUCT * ads )
{
2007-09-29 10:44:39 +04:00
gss_ctx_id_t context_handle = ( gss_ctx_id_t ) ads - > ldap . wrap_private_data ;
2007-07-18 12:19:13 +04:00
ADS_STATUS status ;
int gss_rc ;
uint32 minor_status ;
gss_buffer_desc unwrapped , wrapped ;
int conf_state ;
wrapped . value = ads - > ldap . in . buf + 4 ;
wrapped . length = ads - > ldap . in . ofs - 4 ;
gss_rc = gss_unwrap ( & minor_status , context_handle ,
& wrapped , & unwrapped ,
& conf_state , GSS_C_QOP_DEFAULT ) ;
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
if ( ! ADS_ERR_OK ( status ) ) return status ;
if ( ads - > ldap . wrap_type = = ADS_SASLWRAP_TYPE_SEAL & & conf_state = = 0 ) {
return ADS_ERROR_NT ( NT_STATUS_ACCESS_DENIED ) ;
}
2007-12-06 11:53:42 +03:00
if ( wrapped . length < unwrapped . length ) {
2007-07-18 12:19:13 +04:00
return ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
}
/* copy the wrapped blob to the right location */
memcpy ( ads - > ldap . in . buf + 4 , unwrapped . value , unwrapped . length ) ;
/* set how many bytes must be written to the underlying socket */
ads - > ldap . in . left = unwrapped . length ;
ads - > ldap . in . ofs = 4 ;
gss_release_buffer ( & minor_status , & unwrapped ) ;
return ADS_SUCCESS ;
}
static void ads_sasl_gssapi_disconnect ( ADS_STRUCT * ads )
{
2007-09-29 10:44:39 +04:00
gss_ctx_id_t context_handle = ( gss_ctx_id_t ) ads - > ldap . wrap_private_data ;
2007-07-18 12:19:13 +04:00
uint32 minor_status ;
gss_delete_sec_context ( & minor_status , & context_handle , GSS_C_NO_BUFFER ) ;
ads - > ldap . wrap_ops = NULL ;
ads - > ldap . wrap_private_data = NULL ;
}
static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
. name = " gssapi " ,
. wrap = ads_sasl_gssapi_wrap ,
. unwrap = ads_sasl_gssapi_unwrap ,
. disconnect = ads_sasl_gssapi_disconnect
} ;
2007-07-25 14:34:16 +04:00
/*
perform a LDAP / SASL / SPNEGO / GSSKRB5 bind
*/
2007-07-31 13:31:47 +04:00
static ADS_STATUS ads_sasl_spnego_gsskrb5_bind ( ADS_STRUCT * ads , const gss_name_t serv_name )
2007-07-25 14:34:16 +04:00
{
ADS_STATUS status ;
2007-10-19 04:40:25 +04:00
bool ok ;
2007-07-25 14:34:16 +04:00
uint32 minor_status ;
int gss_rc , rc ;
gss_OID_desc krb5_mech_type =
2011-05-06 01:36:55 +04:00
{ 9 , discard_const_p ( char , " \x2a \x86 \x48 \x86 \xf7 \x12 \x01 \x02 \x02 " ) } ;
2007-07-25 14:34:16 +04:00
gss_OID mech_type = & krb5_mech_type ;
gss_OID actual_mech_type = GSS_C_NULL_OID ;
const char * spnego_mechs [ ] = { OID_KERBEROS5_OLD , OID_KERBEROS5 , OID_NTLMSSP , NULL } ;
gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT ;
gss_buffer_desc input_token , output_token ;
uint32 req_flags , ret_flags ;
uint32 req_tmp , ret_tmp ;
DATA_BLOB unwrapped ;
DATA_BLOB wrapped ;
struct berval cred , * scred = NULL ;
input_token . value = NULL ;
input_token . length = 0 ;
req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG ;
switch ( ads - > ldap . wrap_type ) {
case ADS_SASLWRAP_TYPE_SEAL :
req_flags | = GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG ;
break ;
case ADS_SASLWRAP_TYPE_SIGN :
req_flags | = GSS_C_INTEG_FLAG ;
break ;
case ADS_SASLWRAP_TYPE_PLAIN :
break ;
}
/* Note: here we explicit ask for the krb5 mech_type */
gss_rc = gss_init_sec_context ( & minor_status ,
GSS_C_NO_CREDENTIAL ,
& context_handle ,
serv_name ,
mech_type ,
req_flags ,
0 ,
NULL ,
& input_token ,
& actual_mech_type ,
& output_token ,
& ret_flags ,
NULL ) ;
if ( gss_rc & & gss_rc ! = GSS_S_CONTINUE_NEEDED ) {
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
goto failed ;
}
/*
* As some gssapi krb5 mech implementations
* automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
* to req_flags internaly , it ' s not possible to
* use plain or signing only connection via
* the gssapi interface .
*
* Because of this we need to check it the ret_flags
* has more flags as req_flags and correct the value
* of ads - > ldap . wrap_type .
*
* I ads - > auth . flags has ADS_AUTH_SASL_FORCE
* we need to give an error .
*/
req_tmp = req_flags & ( GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG ) ;
ret_tmp = ret_flags & ( GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG ) ;
if ( req_tmp = = ret_tmp ) {
/* everythings fine... */
} else if ( req_flags & GSS_C_CONF_FLAG ) {
/*
* here we wanted sealing but didn ' t got it
* from the gssapi library
*/
status = ADS_ERROR_NT ( NT_STATUS_NOT_SUPPORTED ) ;
goto failed ;
2007-07-27 11:19:36 +04:00
} else if ( ( req_flags & GSS_C_INTEG_FLAG ) & &
! ( ret_flags & GSS_C_INTEG_FLAG ) ) {
2007-07-25 14:34:16 +04:00
/*
* here we wanted siging but didn ' t got it
* from the gssapi library
*/
status = ADS_ERROR_NT ( NT_STATUS_NOT_SUPPORTED ) ;
goto failed ;
} else if ( ret_flags & GSS_C_CONF_FLAG ) {
/*
* here we didn ' t want sealing
* but the gssapi library forces it
* so correct the needed wrap_type if
* the caller didn ' t forced siging only
*/
if ( ads - > auth . flags & ADS_AUTH_SASL_FORCE ) {
status = ADS_ERROR_NT ( NT_STATUS_NOT_SUPPORTED ) ;
goto failed ;
}
ads - > ldap . wrap_type = ADS_SASLWRAP_TYPE_SEAL ;
req_flags = ret_flags ;
} else if ( ret_flags & GSS_C_INTEG_FLAG ) {
/*
* here we didn ' t want signing
* but the gssapi library forces it
* so correct the needed wrap_type if
* the caller didn ' t forced plain
*/
if ( ads - > auth . flags & ADS_AUTH_SASL_FORCE ) {
status = ADS_ERROR_NT ( NT_STATUS_NOT_SUPPORTED ) ;
goto failed ;
}
ads - > ldap . wrap_type = ADS_SASLWRAP_TYPE_SIGN ;
req_flags = ret_flags ;
} else {
/*
* This could ( should ? ) not happen
*/
status = ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
goto failed ;
}
/* and wrap that in a shiny SPNEGO wrapper */
unwrapped = data_blob_const ( output_token . value , output_token . length ) ;
2010-07-21 01:59:31 +04:00
wrapped = spnego_gen_negTokenInit ( talloc_tos ( ) ,
spnego_mechs , & unwrapped , NULL ) ;
2007-07-25 14:34:16 +04:00
gss_release_buffer ( & minor_status , & output_token ) ;
if ( unwrapped . length > wrapped . length ) {
status = ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
goto failed ;
}
cred . bv_val = ( char * ) wrapped . data ;
cred . bv_len = wrapped . length ;
rc = ldap_sasl_bind_s ( ads - > ldap . ld , NULL , " GSS-SPNEGO " , & cred , NULL , NULL ,
& scred ) ;
data_blob_free ( & wrapped ) ;
if ( rc ! = LDAP_SUCCESS ) {
status = ADS_ERROR ( rc ) ;
goto failed ;
}
if ( scred ) {
wrapped = data_blob_const ( scred - > bv_val , scred - > bv_len ) ;
} else {
wrapped = data_blob_null ;
}
2010-07-21 03:17:58 +04:00
ok = spnego_parse_auth_response ( talloc_tos ( ) , wrapped , NT_STATUS_OK ,
2007-07-25 14:34:16 +04:00
OID_KERBEROS5_OLD ,
& unwrapped ) ;
if ( scred ) ber_bvfree ( scred ) ;
if ( ! ok ) {
status = ADS_ERROR_NT ( NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
goto failed ;
}
input_token . value = unwrapped . data ;
input_token . length = unwrapped . length ;
/*
* As we asked for mutal authentication
* we need to pass the servers response
* to gssapi
*/
gss_rc = gss_init_sec_context ( & minor_status ,
GSS_C_NO_CREDENTIAL ,
& context_handle ,
serv_name ,
mech_type ,
req_flags ,
0 ,
NULL ,
& input_token ,
& actual_mech_type ,
& output_token ,
& ret_flags ,
NULL ) ;
data_blob_free ( & unwrapped ) ;
if ( gss_rc ) {
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
goto failed ;
}
gss_release_buffer ( & minor_status , & output_token ) ;
/*
* If we the sign and seal options
* doesn ' t match after getting the response
* from the server , we don ' t want to use the connection
*/
req_tmp = req_flags & ( GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG ) ;
ret_tmp = ret_flags & ( GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG ) ;
if ( req_tmp ! = ret_tmp ) {
/* everythings fine... */
status = ADS_ERROR_NT ( NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
goto failed ;
}
if ( ads - > ldap . wrap_type > ADS_SASLWRAP_TYPE_PLAIN ) {
2007-08-02 21:41:47 +04:00
uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED ;
2007-07-25 14:34:16 +04:00
gss_rc = gss_wrap_size_limit ( & minor_status , context_handle ,
( ads - > ldap . wrap_type = = ADS_SASLWRAP_TYPE_SEAL ) ,
GSS_C_QOP_DEFAULT ,
2007-08-02 21:41:47 +04:00
max_msg_size , & ads - > ldap . out . max_unwrapped ) ;
2007-07-25 14:34:16 +04:00
if ( gss_rc ) {
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
goto failed ;
}
2007-08-02 21:41:47 +04:00
ads - > ldap . out . sig_size = max_msg_size - ads - > ldap . out . max_unwrapped ;
ads - > ldap . in . min_wrapped = 0x2C ; /* taken from a capture with LDAP unbind */
ads - > ldap . in . max_wrapped = max_msg_size ;
2007-07-31 16:27:25 +04:00
status = ads_setup_sasl_wrapping ( ads , & ads_sasl_gssapi_ops , context_handle ) ;
if ( ! ADS_ERR_OK ( status ) ) {
2007-07-31 16:30:37 +04:00
DEBUG ( 0 , ( " ads_setup_sasl_wrapping() failed: %s \n " ,
2007-07-31 16:27:25 +04:00
ads_errstr ( status ) ) ) ;
goto failed ;
}
2007-07-25 14:34:16 +04:00
/* make sure we don't free context_handle */
context_handle = GSS_C_NO_CONTEXT ;
}
2008-03-23 21:30:47 +03:00
status = ADS_SUCCESS ;
2007-07-25 14:34:16 +04:00
failed :
if ( context_handle ! = GSS_C_NO_CONTEXT )
gss_delete_sec_context ( & minor_status , & context_handle , GSS_C_NO_BUFFER ) ;
return status ;
}
2012-02-13 04:23:15 +04:00
# endif /* HAVE_KRB5 */
2007-07-18 12:19:13 +04:00
2006-12-12 23:27:01 +03:00
# ifdef HAVE_KRB5
2007-07-31 13:31:47 +04:00
struct ads_service_principal {
char * string ;
2012-02-13 04:23:15 +04:00
# ifdef HAVE_KRB5
2007-07-31 13:31:47 +04:00
gss_name_t name ;
# endif
} ;
static void ads_free_service_principal ( struct ads_service_principal * p )
{
SAFE_FREE ( p - > string ) ;
2012-02-13 04:23:15 +04:00
# ifdef HAVE_KRB5
2007-07-31 13:31:47 +04:00
if ( p - > name ) {
uint32 minor_status ;
gss_release_name ( & minor_status , & p - > name ) ;
}
# endif
ZERO_STRUCTP ( p ) ;
}
2011-02-11 13:14:27 +03:00
static ADS_STATUS ads_guess_service_principal ( ADS_STRUCT * ads ,
char * * returned_principal )
{
char * princ = NULL ;
if ( ads - > server . realm & & ads - > server . ldap_server ) {
char * server , * server_realm ;
server = SMB_STRDUP ( ads - > server . ldap_server ) ;
server_realm = SMB_STRDUP ( ads - > server . realm ) ;
if ( ! server | | ! server_realm ) {
SAFE_FREE ( server ) ;
SAFE_FREE ( server_realm ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
strlower_m ( server ) ;
strupper_m ( server_realm ) ;
if ( asprintf ( & princ , " ldap/%s@%s " , server , server_realm ) = = - 1 ) {
SAFE_FREE ( server ) ;
SAFE_FREE ( server_realm ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
SAFE_FREE ( server ) ;
SAFE_FREE ( server_realm ) ;
if ( ! princ ) {
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
} else if ( ads - > config . realm & & ads - > config . ldap_server_name ) {
char * server , * server_realm ;
server = SMB_STRDUP ( ads - > config . ldap_server_name ) ;
server_realm = SMB_STRDUP ( ads - > config . realm ) ;
if ( ! server | | ! server_realm ) {
SAFE_FREE ( server ) ;
SAFE_FREE ( server_realm ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
strlower_m ( server ) ;
strupper_m ( server_realm ) ;
if ( asprintf ( & princ , " ldap/%s@%s " , server , server_realm ) = = - 1 ) {
SAFE_FREE ( server ) ;
SAFE_FREE ( server_realm ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
SAFE_FREE ( server ) ;
SAFE_FREE ( server_realm ) ;
if ( ! princ ) {
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
}
if ( ! princ ) {
return ADS_ERROR ( LDAP_PARAM_ERROR ) ;
}
* returned_principal = princ ;
return ADS_SUCCESS ;
}
2007-07-31 13:31:47 +04:00
static ADS_STATUS ads_generate_service_principal ( ADS_STRUCT * ads ,
const char * given_principal ,
struct ads_service_principal * p )
{
ADS_STATUS status ;
2012-02-13 04:23:15 +04:00
# ifdef HAVE_KRB5
2007-07-31 13:31:47 +04:00
gss_buffer_desc input_name ;
2007-09-13 19:59:46 +04:00
/* GSS_KRB5_NT_PRINCIPAL_NAME */
gss_OID_desc nt_principal =
2011-05-06 01:36:55 +04:00
{ 10 , discard_const_p ( char , " \x2a \x86 \x48 \x86 \xf7 \x12 \x01 \x02 \x02 \x01 " ) } ;
2007-07-31 13:31:47 +04:00
uint32 minor_status ;
int gss_rc ;
# endif
ZERO_STRUCTP ( p ) ;
2007-08-30 19:39:51 +04:00
/* I've seen a child Windows 2000 domain not send
the principal name back in the first round of
2007-07-31 13:31:47 +04:00
the SASL bind reply . So we guess based on server
name and realm . - - jerry */
2010-12-04 05:48:37 +03:00
/* Also try best guess when we get the w2k8 ignore principal
back , or when we are configured to ignore it - gd ,
abartlet */
2007-07-31 13:37:25 +04:00
2010-12-04 05:48:37 +03:00
if ( ! lp_client_use_spnego_principal ( ) | |
! given_principal | |
2007-08-30 19:39:51 +04:00
strequal ( given_principal , ADS_IGNORE_PRINCIPAL ) ) {
2007-07-31 13:37:25 +04:00
2007-09-12 03:35:17 +04:00
status = ads_guess_service_principal ( ads , & p - > string ) ;
2007-08-30 19:39:51 +04:00
if ( ! ADS_ERR_OK ( status ) ) {
return status ;
2007-07-31 13:37:25 +04:00
}
2007-08-30 19:39:51 +04:00
} else {
p - > string = SMB_STRDUP ( given_principal ) ;
2007-07-31 13:31:47 +04:00
if ( ! p - > string ) {
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
}
2012-02-13 04:23:15 +04:00
# ifdef HAVE_KRB5
2007-09-13 19:59:46 +04:00
input_name . value = p - > string ;
input_name . length = strlen ( p - > string ) ;
2007-07-31 13:31:47 +04:00
gss_rc = gss_import_name ( & minor_status , & input_name , & nt_principal , & p - > name ) ;
if ( gss_rc ) {
ads_free_service_principal ( p ) ;
return ADS_ERROR_GSS ( gss_rc , minor_status ) ;
}
# endif
2007-09-13 19:59:46 +04:00
return ADS_SUCCESS ;
2007-07-31 13:31:47 +04:00
}
2002-09-25 19:19:00 +04:00
/*
perform a LDAP / SASL / SPNEGO / KRB5 bind
*/
2007-07-25 14:34:16 +04:00
static ADS_STATUS ads_sasl_spnego_rawkrb5_bind ( ADS_STRUCT * ads , const char * principal )
2002-09-25 19:19:00 +04:00
{
2007-05-14 16:16:20 +04:00
DATA_BLOB blob = data_blob_null ;
2004-05-07 03:08:56 +04:00
struct berval cred , * scred = NULL ;
2007-05-14 16:16:20 +04:00
DATA_BLOB session_key = data_blob_null ;
2002-09-25 19:19:00 +04:00
int rc ;
2007-07-25 14:34:16 +04:00
if ( ads - > ldap . wrap_type > ADS_SASLWRAP_TYPE_PLAIN ) {
return ADS_ERROR_NT ( NT_STATUS_NOT_SUPPORTED ) ;
}
2010-07-21 01:59:31 +04:00
rc = spnego_gen_krb5_negTokenInit ( talloc_tos ( ) , principal ,
ads - > auth . time_offset , & blob , & session_key , 0 ,
2007-02-08 20:02:39 +03:00
& ads - > auth . tgs_expire ) ;
2002-09-25 19:19:00 +04:00
2004-01-08 11:19:18 +03:00
if ( rc ) {
return ADS_ERROR_KRB5 ( rc ) ;
2002-09-25 19:19:00 +04:00
}
/* now send the auth packet and we should be done */
2003-08-15 08:42:05 +04:00
cred . bv_val = ( char * ) blob . data ;
2002-09-25 19:19:00 +04:00
cred . bv_len = blob . length ;
2007-07-16 15:08:00 +04:00
rc = ldap_sasl_bind_s ( ads - > ldap . ld , NULL , " GSS-SPNEGO " , & cred , NULL , NULL , & scred ) ;
2002-09-25 19:19:00 +04:00
data_blob_free ( & blob ) ;
2004-01-08 11:19:18 +03:00
data_blob_free ( & session_key ) ;
2004-05-07 03:08:56 +04:00
if ( scred )
ber_bvfree ( scred ) ;
2002-09-25 19:19:00 +04:00
return ADS_ERROR ( rc ) ;
}
2007-07-25 14:34:16 +04:00
2007-07-31 13:31:47 +04:00
static ADS_STATUS ads_sasl_spnego_krb5_bind ( ADS_STRUCT * ads ,
struct ads_service_principal * p )
2007-07-25 14:34:16 +04:00
{
2012-02-13 04:23:15 +04:00
# ifdef HAVE_KRB5
2007-07-25 14:34:16 +04:00
/*
* we only use the gsskrb5 based implementation
* when sasl sign or seal is requested .
*
* This has the following reasons :
* - it ' s likely that the gssapi krb5 mech implementation
* doesn ' t support to negotiate plain connections
* - the ads_sasl_spnego_rawkrb5_bind is more robust
* against clock skew errors
*/
if ( ads - > ldap . wrap_type > ADS_SASLWRAP_TYPE_PLAIN ) {
2007-07-31 13:31:47 +04:00
return ads_sasl_spnego_gsskrb5_bind ( ads , p - > name ) ;
2007-07-25 14:34:16 +04:00
}
# endif
2007-07-31 13:31:47 +04:00
return ads_sasl_spnego_rawkrb5_bind ( ads , p - > string ) ;
2007-07-25 14:34:16 +04:00
}
2007-09-12 03:21:50 +04:00
# endif /* HAVE_KRB5 */
2002-09-25 19:19:00 +04:00
/*
this performs a SASL / SPNEGO bind
*/
static ADS_STATUS ads_sasl_spnego_bind ( ADS_STRUCT * ads )
{
struct berval * scred = NULL ;
int rc , i ;
ADS_STATUS status ;
DATA_BLOB blob ;
2007-07-31 13:31:47 +04:00
char * given_principal = NULL ;
2002-09-25 19:19:00 +04:00
char * OIDs [ ASN1_MAX_OIDS ] ;
2005-03-22 18:12:50 +03:00
# ifdef HAVE_KRB5
2007-10-19 04:40:25 +04:00
bool got_kerberos_mechanism = False ;
2005-03-22 18:12:50 +03:00
# endif
2002-09-25 19:19:00 +04:00
2007-07-16 15:08:00 +04:00
rc = ldap_sasl_bind_s ( ads - > ldap . ld , NULL , " GSS-SPNEGO " , NULL , NULL , NULL , & scred ) ;
2002-09-25 19:19:00 +04:00
if ( rc ! = LDAP_SASL_BIND_IN_PROGRESS ) {
status = ADS_ERROR ( rc ) ;
goto failed ;
}
blob = data_blob ( scred - > bv_val , scred - > bv_len ) ;
2004-01-08 11:19:18 +03:00
ber_bvfree ( scred ) ;
2002-09-25 19:19:00 +04:00
#if 0
file_save ( " sasl_spnego.dat " , blob . data , blob . length ) ;
2001-12-08 14:18:56 +03:00
# endif
2002-09-25 19:19:00 +04:00
/* the server sent us the first part of the SPNEGO exchange in the negprot
reply */
2010-09-24 08:44:24 +04:00
if ( ! spnego_parse_negTokenInit ( talloc_tos ( ) , blob , OIDs , & given_principal , NULL ) | |
OIDs [ 0 ] = = NULL ) {
2002-09-25 19:19:00 +04:00
data_blob_free ( & blob ) ;
status = ADS_ERROR ( LDAP_OPERATIONS_ERROR ) ;
goto failed ;
}
data_blob_free ( & blob ) ;
/* make sure the server understands kerberos */
for ( i = 0 ; OIDs [ i ] ; i + + ) {
2004-07-07 22:12:09 +04:00
DEBUG ( 3 , ( " ads_sasl_spnego_bind: got OID=%s \n " , OIDs [ i ] ) ) ;
2005-03-22 18:12:50 +03:00
# ifdef HAVE_KRB5
2002-09-25 19:19:00 +04:00
if ( strcmp ( OIDs [ i ] , OID_KERBEROS5_OLD ) = = 0 | |
strcmp ( OIDs [ i ] , OID_KERBEROS5 ) = = 0 ) {
got_kerberos_mechanism = True ;
}
2005-03-22 18:12:50 +03:00
# endif
2008-10-22 16:06:08 +04:00
talloc_free ( OIDs [ i ] ) ;
2002-09-25 19:19:00 +04:00
}
2007-09-28 22:15:34 +04:00
DEBUG ( 3 , ( " ads_sasl_spnego_bind: got server principal name = %s \n " , given_principal ) ) ;
2001-12-08 14:18:56 +03:00
2002-10-01 22:26:00 +04:00
# ifdef HAVE_KRB5
2002-09-25 19:19:00 +04:00
if ( ! ( ads - > auth . flags & ADS_AUTH_DISABLE_KERBEROS ) & &
2007-02-10 23:29:09 +03:00
got_kerberos_mechanism )
{
2007-07-31 13:31:47 +04:00
struct ads_service_principal p ;
status = ads_generate_service_principal ( ads , given_principal , & p ) ;
2008-10-22 16:06:08 +04:00
TALLOC_FREE ( given_principal ) ;
2007-07-31 13:31:47 +04:00
if ( ! ADS_ERR_OK ( status ) ) {
return status ;
2007-02-10 23:29:09 +03:00
}
2007-07-31 13:31:47 +04:00
status = ads_sasl_spnego_krb5_bind ( ads , & p ) ;
2004-05-07 03:08:56 +04:00
if ( ADS_ERR_OK ( status ) ) {
2007-07-31 13:31:47 +04:00
ads_free_service_principal ( & p ) ;
2002-11-10 06:07:19 +03:00
return status ;
2004-05-07 03:08:56 +04:00
}
2004-01-08 11:19:18 +03:00
2007-02-08 20:02:39 +03:00
DEBUG ( 10 , ( " ads_sasl_spnego_krb5_bind failed with: %s, "
" calling kinit \n " , ads_errstr ( status ) ) ) ;
2004-01-08 11:19:18 +03:00
status = ADS_ERROR_KRB5 ( ads_kinit_password ( ads ) ) ;
if ( ADS_ERR_OK ( status ) ) {
2007-07-31 13:31:47 +04:00
status = ads_sasl_spnego_krb5_bind ( ads , & p ) ;
2007-09-26 05:02:52 +04:00
if ( ! ADS_ERR_OK ( status ) ) {
DEBUG ( 0 , ( " kinit succeeded but "
" ads_sasl_spnego_krb5_bind failed: %s \n " ,
ads_errstr ( status ) ) ) ;
}
2003-04-24 18:07:13 +04:00
}
2004-01-08 11:19:18 +03:00
2007-07-31 13:31:47 +04:00
ads_free_service_principal ( & p ) ;
2003-06-10 07:47:42 +04:00
/* only fallback to NTLMSSP if allowed */
if ( ADS_ERR_OK ( status ) | |
! ( ads - > auth . flags & ADS_AUTH_ALLOW_NTLMSSP ) ) {
2003-04-24 18:07:13 +04:00
return status ;
2003-06-10 07:47:42 +04:00
}
2007-07-31 13:31:47 +04:00
} else
2002-10-01 22:26:00 +04:00
# endif
2007-07-31 13:31:47 +04:00
{
2008-10-22 16:06:08 +04:00
TALLOC_FREE ( given_principal ) ;
2007-07-31 13:31:47 +04:00
}
2004-05-07 03:08:56 +04:00
2002-09-25 19:19:00 +04:00
/* lets do NTLMSSP ... this has the big advantage that we don't need
to sync clocks , and we don ' t rely on special versions of the krb5
library for HMAC_MD4 encryption */
return ads_sasl_spnego_ntlmssp_bind ( ads ) ;
failed :
return status ;
}
2012-02-13 04:23:15 +04:00
# ifdef HAVE_KRB5
2001-12-08 14:18:56 +03:00
# define MAX_GSS_PASSES 3
/* this performs a SASL/gssapi bind
we avoid using cyrus - sasl to make Samba more robust . cyrus - sasl
is very dependent on correctly configured DNS whereas
this routine is much less fragile
2002-09-25 19:19:00 +04:00
see RFC2078 and RFC2222 for details
2001-12-08 14:18:56 +03:00
*/
2007-07-31 13:49:14 +04:00
static ADS_STATUS ads_sasl_gssapi_do_bind ( ADS_STRUCT * ads , const gss_name_t serv_name )
2001-12-08 14:18:56 +03:00
{
2003-08-16 01:19:34 +04:00
uint32 minor_status ;
2007-04-04 18:50:39 +04:00
gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT ;
2001-12-08 14:18:56 +03:00
gss_OID mech_type = GSS_C_NULL_OID ;
gss_buffer_desc output_token , input_token ;
2007-07-18 12:19:13 +04:00
uint32 req_flags , ret_flags ;
int conf_state ;
2001-12-08 14:18:56 +03:00
struct berval cred ;
2004-05-07 03:08:56 +04:00
struct berval * scred = NULL ;
2001-12-08 14:18:56 +03:00
int i = 0 ;
2001-12-19 15:21:12 +03:00
int gss_rc , rc ;
2001-12-08 14:18:56 +03:00
uint8 * p ;
2007-08-02 21:41:47 +04:00
uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED ;
uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN ;
2001-12-19 15:21:12 +03:00
ADS_STATUS status ;
2001-12-08 14:18:56 +03:00
input_token . value = NULL ;
input_token . length = 0 ;
2007-08-02 21:41:47 +04:00
/*
* Note : here we always ask the gssapi for sign and seal
* as this is negotiated later after the mutal
* authentication
*/
req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG ;
2007-07-18 12:19:13 +04:00
2001-12-08 14:18:56 +03:00
for ( i = 0 ; i < MAX_GSS_PASSES ; i + + ) {
gss_rc = gss_init_sec_context ( & minor_status ,
GSS_C_NO_CREDENTIAL ,
& context_handle ,
serv_name ,
mech_type ,
2007-07-18 12:19:13 +04:00
req_flags ,
2001-12-08 14:18:56 +03:00
0 ,
NULL ,
& input_token ,
NULL ,
& output_token ,
& ret_flags ,
NULL ) ;
2007-08-02 19:11:37 +04:00
if ( scred ) {
ber_bvfree ( scred ) ;
scred = NULL ;
2001-12-08 14:18:56 +03:00
}
2001-12-19 11:44:23 +03:00
if ( gss_rc & & gss_rc ! = GSS_S_CONTINUE_NEEDED ) {
2001-12-19 15:21:12 +03:00
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
goto failed ;
2001-12-19 11:44:23 +03:00
}
2001-12-08 14:18:56 +03:00
2006-09-04 23:47:48 +04:00
cred . bv_val = ( char * ) output_token . value ;
2001-12-08 14:18:56 +03:00
cred . bv_len = output_token . length ;
2007-07-16 15:08:00 +04:00
rc = ldap_sasl_bind_s ( ads - > ldap . ld , NULL , " GSSAPI " , & cred , NULL , NULL ,
2001-12-08 14:18:56 +03:00
& scred ) ;
2001-12-19 15:21:12 +03:00
if ( rc ! = LDAP_SASL_BIND_IN_PROGRESS ) {
status = ADS_ERROR ( rc ) ;
goto failed ;
}
2001-12-08 14:18:56 +03:00
if ( output_token . value ) {
gss_release_buffer ( & minor_status , & output_token ) ;
}
if ( scred ) {
input_token . value = scred - > bv_val ;
input_token . length = scred - > bv_len ;
} else {
input_token . value = NULL ;
input_token . length = 0 ;
}
2001-12-19 15:21:12 +03:00
if ( gss_rc = = 0 ) break ;
2001-12-08 14:18:56 +03:00
}
gss_rc = gss_unwrap ( & minor_status , context_handle , & input_token , & output_token ,
2007-07-18 12:19:13 +04:00
& conf_state , NULL ) ;
2007-08-02 19:11:37 +04:00
if ( scred ) {
ber_bvfree ( scred ) ;
scred = NULL ;
}
2001-12-19 15:21:12 +03:00
if ( gss_rc ) {
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
goto failed ;
}
2001-12-08 14:18:56 +03:00
p = ( uint8 * ) output_token . value ;
2006-02-04 01:19:41 +03:00
#if 0
2002-09-25 19:19:00 +04:00
file_save ( " sasl_gssapi.dat " , output_token . value , output_token . length ) ;
2006-02-04 01:19:41 +03:00
# endif
2006-08-29 04:53:28 +04:00
if ( p ) {
2007-08-02 21:41:47 +04:00
wrap_type = CVAL ( p , 0 ) ;
SCVAL ( p , 0 , 0 ) ;
max_msg_size = RIVAL ( p , 0 ) ;
2006-08-29 04:53:28 +04:00
}
2001-12-08 14:18:56 +03:00
gss_release_buffer ( & minor_status , & output_token ) ;
2007-08-02 21:41:47 +04:00
if ( ! ( wrap_type & ads - > ldap . wrap_type ) ) {
/*
* the server doesn ' t supports the wrap
* type we want : - (
*/
DEBUG ( 0 , ( " The ldap sasl wrap type doesn't match wanted[%d] server[%d] \n " ,
ads - > ldap . wrap_type , wrap_type ) ) ;
DEBUGADD ( 0 , ( " You may want to set the 'client ldap sasl wrapping' option \n " ) ) ;
status = ADS_ERROR_NT ( NT_STATUS_NOT_SUPPORTED ) ;
goto failed ;
}
/* 0x58 is the minimum windows accepts */
if ( max_msg_size < 0x58 ) {
max_msg_size = 0x58 ;
}
2007-04-10 20:04:22 +04:00
output_token . length = 4 ;
output_token . value = SMB_MALLOC ( output_token . length ) ;
2010-09-10 02:29:03 +04:00
if ( ! output_token . value ) {
output_token . length = 0 ;
status = ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
goto failed ;
}
2006-09-04 23:47:48 +04:00
p = ( uint8 * ) output_token . value ;
2001-12-08 14:18:56 +03:00
2007-08-02 21:41:47 +04:00
RSIVAL ( p , 0 , max_msg_size ) ;
SCVAL ( p , 0 , ads - > ldap . wrap_type ) ;
2007-04-10 20:04:22 +04:00
/*
* we used to add sprintf ( " dn:%s " , ads - > config . bind_path ) here .
* but using ads - > config . bind_path is the wrong ! It should be
* the DN of the user object !
*
* w2k3 gives an error when we send an incorrect DN , but sending nothing
* is ok and matches the information flow used in GSS - SPNEGO .
*/
2001-12-08 14:18:56 +03:00
2001-12-19 15:21:12 +03:00
gss_rc = gss_wrap ( & minor_status , context_handle , 0 , GSS_C_QOP_DEFAULT ,
2010-09-10 02:29:03 +04:00
& output_token , /* used as *input* here. */
& conf_state ,
& input_token ) ; /* Used as *output* here. */
2001-12-19 15:21:12 +03:00
if ( gss_rc ) {
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
2010-09-10 02:29:03 +04:00
output_token . length = 0 ;
SAFE_FREE ( output_token . value ) ;
2001-12-19 15:21:12 +03:00
goto failed ;
}
2001-12-08 14:18:56 +03:00
2010-09-10 02:29:03 +04:00
/* We've finished with output_token. */
SAFE_FREE ( output_token . value ) ;
output_token . length = 0 ;
2001-12-08 14:18:56 +03:00
2006-09-04 23:47:48 +04:00
cred . bv_val = ( char * ) input_token . value ;
2001-12-08 14:18:56 +03:00
cred . bv_len = input_token . length ;
2007-07-16 15:08:00 +04:00
rc = ldap_sasl_bind_s ( ads - > ldap . ld , NULL , " GSSAPI " , & cred , NULL , NULL ,
2001-12-08 14:18:56 +03:00
& scred ) ;
gss_release_buffer ( & minor_status , & input_token ) ;
2007-07-25 11:23:20 +04:00
status = ADS_ERROR ( rc ) ;
if ( ! ADS_ERR_OK ( status ) ) {
goto failed ;
}
2001-12-08 14:18:56 +03:00
2007-07-18 12:19:13 +04:00
if ( ads - > ldap . wrap_type > ADS_SASLWRAP_TYPE_PLAIN ) {
gss_rc = gss_wrap_size_limit ( & minor_status , context_handle ,
( ads - > ldap . wrap_type = = ADS_SASLWRAP_TYPE_SEAL ) ,
GSS_C_QOP_DEFAULT ,
2007-08-02 21:41:47 +04:00
max_msg_size , & ads - > ldap . out . max_unwrapped ) ;
2007-07-18 12:19:13 +04:00
if ( gss_rc ) {
status = ADS_ERROR_GSS ( gss_rc , minor_status ) ;
goto failed ;
}
2007-08-02 21:41:47 +04:00
ads - > ldap . out . sig_size = max_msg_size - ads - > ldap . out . max_unwrapped ;
ads - > ldap . in . min_wrapped = 0x2C ; /* taken from a capture with LDAP unbind */
ads - > ldap . in . max_wrapped = max_msg_size ;
2007-07-31 16:27:25 +04:00
status = ads_setup_sasl_wrapping ( ads , & ads_sasl_gssapi_ops , context_handle ) ;
if ( ! ADS_ERR_OK ( status ) ) {
2007-07-31 16:30:37 +04:00
DEBUG ( 0 , ( " ads_setup_sasl_wrapping() failed: %s \n " ,
2007-07-31 16:27:25 +04:00
ads_errstr ( status ) ) ) ;
goto failed ;
}
2007-07-18 12:19:13 +04:00
/* make sure we don't free context_handle */
context_handle = GSS_C_NO_CONTEXT ;
}
2007-08-02 21:41:47 +04:00
2001-12-08 14:18:56 +03:00
failed :
2007-03-16 01:11:13 +03:00
2007-04-04 18:50:39 +04:00
if ( context_handle ! = GSS_C_NO_CONTEXT )
2007-04-04 21:38:12 +04:00
gss_delete_sec_context ( & minor_status , & context_handle , GSS_C_NO_BUFFER ) ;
2007-03-16 01:11:13 +03:00
2004-05-07 03:08:56 +04:00
if ( scred )
ber_bvfree ( scred ) ;
2001-12-19 15:21:12 +03:00
return status ;
2001-12-08 14:18:56 +03:00
}
2007-07-31 13:49:14 +04:00
static ADS_STATUS ads_sasl_gssapi_bind ( ADS_STRUCT * ads )
{
ADS_STATUS status ;
struct ads_service_principal p ;
status = ads_generate_service_principal ( ads , NULL , & p ) ;
if ( ! ADS_ERR_OK ( status ) ) {
return status ;
}
status = ads_sasl_gssapi_do_bind ( ads , p . name ) ;
if ( ADS_ERR_OK ( status ) ) {
ads_free_service_principal ( & p ) ;
return status ;
}
DEBUG ( 10 , ( " ads_sasl_gssapi_do_bind failed with: %s, "
" calling kinit \n " , ads_errstr ( status ) ) ) ;
status = ADS_ERROR_KRB5 ( ads_kinit_password ( ads ) ) ;
if ( ADS_ERR_OK ( status ) ) {
status = ads_sasl_gssapi_do_bind ( ads , p . name ) ;
}
ads_free_service_principal ( & p ) ;
return status ;
}
2012-02-13 04:23:15 +04:00
# endif /* HAVE_KRB5 */
2002-09-25 19:19:00 +04:00
/* mapping between SASL mechanisms and functions */
static struct {
const char * name ;
ADS_STATUS ( * fn ) ( ADS_STRUCT * ) ;
} sasl_mechanisms [ ] = {
{ " GSS-SPNEGO " , ads_sasl_spnego_bind } ,
2012-02-13 04:23:15 +04:00
# ifdef HAVE_KRB5
2002-09-25 19:19:00 +04:00
{ " GSSAPI " , ads_sasl_gssapi_bind } , /* doesn't work with .NET RC1. No idea why */
# endif
{ NULL , NULL }
} ;
2001-12-08 14:18:56 +03:00
2001-12-19 15:21:12 +03:00
ADS_STATUS ads_sasl_bind ( ADS_STRUCT * ads )
2001-12-08 14:18:56 +03:00
{
2002-09-25 19:19:00 +04:00
const char * attrs [ ] = { " supportedSASLMechanisms " , NULL } ;
char * * values ;
ADS_STATUS status ;
int i , j ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res ;
2002-09-25 19:19:00 +04:00
/* get a list of supported SASL mechanisms */
status = ads_do_search ( ads , " " , LDAP_SCOPE_BASE , " (objectclass=*) " , attrs , & res ) ;
if ( ! ADS_ERR_OK ( status ) ) return status ;
2007-07-16 15:08:00 +04:00
values = ldap_get_values ( ads - > ldap . ld , res , " supportedSASLMechanisms " ) ;
2002-09-25 19:19:00 +04:00
2007-07-18 11:45:16 +04:00
if ( ads - > auth . flags & ADS_AUTH_SASL_SEAL ) {
ads - > ldap . wrap_type = ADS_SASLWRAP_TYPE_SEAL ;
} else if ( ads - > auth . flags & ADS_AUTH_SASL_SIGN ) {
ads - > ldap . wrap_type = ADS_SASLWRAP_TYPE_SIGN ;
} else {
ads - > ldap . wrap_type = ADS_SASLWRAP_TYPE_PLAIN ;
}
2002-09-25 19:19:00 +04:00
/* try our supported mechanisms in order */
for ( i = 0 ; sasl_mechanisms [ i ] . name ; i + + ) {
/* see if the server supports it */
for ( j = 0 ; values & & values [ j ] ; j + + ) {
if ( strcmp ( values [ j ] , sasl_mechanisms [ i ] . name ) = = 0 ) {
DEBUG ( 4 , ( " Found SASL mechanism %s \n " , values [ j ] ) ) ;
2010-03-30 11:50:09 +04:00
retry :
2002-09-25 19:19:00 +04:00
status = sasl_mechanisms [ i ] . fn ( ads ) ;
2010-03-30 11:50:09 +04:00
if ( status . error_type = = ENUM_ADS_ERROR_LDAP & &
status . err . rc = = LDAP_STRONG_AUTH_REQUIRED & &
ads - > ldap . wrap_type = = ADS_SASLWRAP_TYPE_PLAIN )
{
DEBUG ( 3 , ( " SASL bin got LDAP_STRONG_AUTH_REQUIRED "
" retrying with signing enabled \n " ) ) ;
ads - > ldap . wrap_type = ADS_SASLWRAP_TYPE_SIGN ;
goto retry ;
}
2002-09-25 19:19:00 +04:00
ldap_value_free ( values ) ;
ldap_msgfree ( res ) ;
return status ;
}
}
}
ldap_value_free ( values ) ;
ldap_msgfree ( res ) ;
return ADS_ERROR ( LDAP_AUTH_METHOD_NOT_SUPPORTED ) ;
2001-12-08 14:18:56 +03:00
}
2006-01-26 00:25:25 +03:00
# endif /* HAVE_LDAP */
2001-12-08 14:18:56 +03:00