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
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"
2002-10-01 22:26:00 +04:00
# ifdef HAVE_LDAP
2001-12-08 14:18:56 +03:00
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
{
2002-09-25 19:19:00 +04:00
const char * mechs [ ] = { OID_NTLMSSP , NULL } ;
DATA_BLOB msg1 ;
DATA_BLOB blob , chal1 , chal2 , auth ;
uint8 challenge [ 8 ] ;
uint8 nthash [ 24 ] , lmhash [ 24 ] , sess_key [ 16 ] ;
uint32 neg_flags ;
struct berval cred , * scred ;
ADS_STATUS status ;
int rc ;
2001-12-08 14:18:56 +03:00
2002-09-25 19:19:00 +04:00
if ( ! ads - > auth . password ) {
/* No password, don't segfault below... */
return ADS_ERROR_NT ( NT_STATUS_LOGON_FAILURE ) ;
2001-12-08 14:18:56 +03:00
}
2002-09-25 19:19:00 +04:00
neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_128 |
NTLMSSP_NEGOTIATE_NTLM ;
memset ( sess_key , 0 , 16 ) ;
/* generate the ntlmssp negotiate packet */
msrpc_gen ( & blob , " CddB " ,
" NTLMSSP " ,
NTLMSSP_NEGOTIATE ,
neg_flags ,
sess_key , 16 ) ;
/* and wrap it in a SPNEGO wrapper */
msg1 = gen_negTokenTarg ( mechs , blob ) ;
data_blob_free ( & blob ) ;
cred . bv_val = msg1 . data ;
cred . bv_len = msg1 . length ;
rc = ldap_sasl_bind_s ( ads - > ld , NULL , " GSS-SPNEGO " , & cred , NULL , NULL , & scred ) ;
if ( rc ! = LDAP_SASL_BIND_IN_PROGRESS ) {
status = ADS_ERROR ( rc ) ;
goto failed ;
}
blob = data_blob ( scred - > bv_val , scred - > bv_len ) ;
/* the server gives us back two challenges */
if ( ! spnego_parse_challenge ( blob , & chal1 , & chal2 ) ) {
DEBUG ( 3 , ( " Failed to parse challenges \n " ) ) ;
status = ADS_ERROR ( LDAP_OPERATIONS_ERROR ) ;
goto failed ;
}
data_blob_free ( & blob ) ;
/* encrypt the password with the challenge */
memcpy ( challenge , chal1 . data + 24 , 8 ) ;
SMBencrypt ( ads - > auth . password , challenge , lmhash ) ;
SMBNTencrypt ( ads - > auth . password , challenge , nthash ) ;
data_blob_free ( & chal1 ) ;
data_blob_free ( & chal2 ) ;
/* this generates the actual auth packet */
msrpc_gen ( & blob , " CdBBUUUBd " ,
" NTLMSSP " ,
NTLMSSP_AUTH ,
lmhash , 24 ,
nthash , 24 ,
lp_workgroup ( ) ,
ads - > auth . user_name ,
2002-11-13 02:20:50 +03:00
global_myname ( ) ,
2002-09-25 19:19:00 +04:00
sess_key , 16 ,
neg_flags ) ;
/* wrap it in SPNEGO */
auth = spnego_gen_auth ( blob ) ;
data_blob_free ( & blob ) ;
/* now send the auth packet and we should be done */
cred . bv_val = auth . data ;
cred . bv_len = auth . length ;
rc = ldap_sasl_bind_s ( ads - > ld , NULL , " GSS-SPNEGO " , & cred , NULL , NULL , & scred ) ;
return ADS_ERROR ( rc ) ;
failed :
return status ;
2001-12-08 14:18:56 +03:00
}
2002-09-25 19:19:00 +04:00
/*
perform a LDAP / SASL / SPNEGO / KRB5 bind
*/
static ADS_STATUS ads_sasl_spnego_krb5_bind ( ADS_STRUCT * ads , const char * principal )
{
DATA_BLOB blob ;
struct berval cred , * scred ;
int rc ;
blob = spnego_gen_negTokenTarg ( principal , ads - > auth . time_offset ) ;
if ( ! blob . data ) {
return ADS_ERROR ( LDAP_OPERATIONS_ERROR ) ;
}
/* now send the auth packet and we should be done */
cred . bv_val = blob . data ;
cred . bv_len = blob . length ;
rc = ldap_sasl_bind_s ( ads - > ld , NULL , " GSS-SPNEGO " , & cred , NULL , NULL , & scred ) ;
data_blob_free ( & blob ) ;
return ADS_ERROR ( rc ) ;
}
/*
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 ;
char * principal ;
char * OIDs [ ASN1_MAX_OIDS ] ;
BOOL got_kerberos_mechanism = False ;
rc = ldap_sasl_bind_s ( ads - > ld , NULL , " GSS-SPNEGO " , NULL , NULL , NULL , & scred ) ;
if ( rc ! = LDAP_SASL_BIND_IN_PROGRESS ) {
status = ADS_ERROR ( rc ) ;
goto failed ;
}
blob = data_blob ( scred - > bv_val , scred - > bv_len ) ;
#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 */
if ( ! spnego_parse_negTokenInit ( blob , OIDs , & principal ) ) {
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 + + ) {
DEBUG ( 3 , ( " got OID=%s \n " , OIDs [ i ] ) ) ;
if ( strcmp ( OIDs [ i ] , OID_KERBEROS5_OLD ) = = 0 | |
strcmp ( OIDs [ i ] , OID_KERBEROS5 ) = = 0 ) {
got_kerberos_mechanism = True ;
}
free ( OIDs [ i ] ) ;
}
DEBUG ( 3 , ( " got principal=%s \n " , 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 ) & &
2002-11-10 06:07:19 +03:00
got_kerberos_mechanism ) {
status = ads_sasl_spnego_krb5_bind ( ads , principal ) ;
if ( ADS_ERR_OK ( status ) )
return status ;
2003-04-24 18:07:13 +04:00
if ( ads_kinit_password ( ads ) = = 0 ) {
2002-11-10 06:07:19 +03:00
status = ads_sasl_spnego_krb5_bind ( ads , principal ) ;
2003-04-24 18:07:13 +04:00
}
if ( ADS_ERR_OK ( status ) )
return status ;
2002-09-25 19:19:00 +04:00
}
2002-10-01 22:26:00 +04:00
# endif
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 ;
}
# ifdef HAVE_GSSAPI
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
*/
2002-09-25 19:19:00 +04:00
static ADS_STATUS ads_sasl_gssapi_bind ( ADS_STRUCT * ads )
2001-12-08 14:18:56 +03:00
{
2001-12-19 11:44:23 +03:00
int minor_status ;
2001-12-08 14:18:56 +03:00
gss_name_t serv_name ;
gss_buffer_desc input_name ;
gss_ctx_id_t context_handle ;
gss_OID mech_type = GSS_C_NULL_OID ;
gss_buffer_desc output_token , input_token ;
OM_uint32 ret_flags , conf_state ;
struct berval cred ;
struct berval * scred ;
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 ;
uint32 max_msg_size ;
char * sname ;
2002-09-25 19:19:00 +04:00
unsigned sec_layer ;
2001-12-19 15:21:12 +03:00
ADS_STATUS status ;
2001-12-19 11:44:23 +03:00
krb5_principal principal ;
krb5_context ctx ;
2003-02-24 05:55:00 +03:00
krb5_enctype enc_types [ ] = {
# ifdef ENCTYPE_ARCFOUR_HMAC
ENCTYPE_ARCFOUR_HMAC ,
# endif
ENCTYPE_DES_CBC_MD5 ,
ENCTYPE_NULL } ;
2001-12-19 11:44:23 +03:00
gss_OID_desc nt_principal =
{ 10 , " \052 \206 \110 \206 \367 \022 \001 \002 \002 \002 " } ;
/* we need to fetch a service ticket as the ldap user in the
servers realm , regardless of our realm */
2002-08-17 21:00:51 +04:00
asprintf ( & sname , " ldap/%s@%s " , ads - > config . ldap_server_name , ads - > config . realm ) ;
2001-12-19 11:44:23 +03:00
krb5_init_context ( & ctx ) ;
krb5_set_default_tgs_ktypes ( ctx , enc_types ) ;
krb5_parse_name ( ctx , sname , & principal ) ;
free ( sname ) ;
krb5_free_context ( ctx ) ;
2001-12-08 14:18:56 +03:00
2001-12-19 11:44:23 +03:00
input_name . value = & principal ;
input_name . length = sizeof ( principal ) ;
2001-12-08 14:18:56 +03:00
2001-12-19 15:21:12 +03:00
gss_rc = gss_import_name ( & minor_status , & input_name , & nt_principal , & serv_name ) ;
if ( gss_rc ) {
return ADS_ERROR_GSS ( gss_rc , minor_status ) ;
}
2001-12-08 14:18:56 +03:00
context_handle = GSS_C_NO_CONTEXT ;
input_token . value = NULL ;
input_token . length = 0 ;
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 ,
GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG ,
0 ,
NULL ,
& input_token ,
NULL ,
& output_token ,
& ret_flags ,
NULL ) ;
if ( input_token . value ) {
gss_release_buffer ( & minor_status , & input_token ) ;
}
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
cred . bv_val = output_token . value ;
cred . bv_len = output_token . length ;
2001-12-19 15:21:12 +03:00
rc = ldap_sasl_bind_s ( ads - > 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_release_name ( & minor_status , & serv_name ) ;
gss_rc = gss_unwrap ( & minor_status , context_handle , & input_token , & output_token ,
& conf_state , 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
gss_release_buffer ( & minor_status , & input_token ) ;
p = ( uint8 * ) output_token . value ;
2002-09-25 19:19:00 +04:00
file_save ( " sasl_gssapi.dat " , output_token . value , output_token . length ) ;
2001-12-08 14:18:56 +03:00
max_msg_size = ( p [ 1 ] < < 16 ) | ( p [ 2 ] < < 8 ) | p [ 3 ] ;
2002-09-25 19:19:00 +04:00
sec_layer = * p ;
2001-12-08 14:18:56 +03:00
gss_release_buffer ( & minor_status , & output_token ) ;
2002-08-17 21:00:51 +04:00
output_token . value = malloc ( strlen ( ads - > config . bind_path ) + 8 ) ;
2001-12-08 14:18:56 +03:00
p = output_token . value ;
2002-09-25 19:19:00 +04:00
* p + + = 1 ; /* no sign & seal selection */
2001-12-08 14:18:56 +03:00
/* choose the same size as the server gave us */
* p + + = max_msg_size > > 16 ;
* p + + = max_msg_size > > 8 ;
* p + + = max_msg_size ;
2002-08-17 21:00:51 +04:00
snprintf ( p , strlen ( ads - > config . bind_path ) + 4 , " dn:%s " , ads - > config . bind_path ) ;
2002-09-25 19:19:00 +04:00
p + = strlen ( p ) ;
2001-12-08 14:18:56 +03:00
2002-09-25 19:19:00 +04:00
output_token . length = PTR_DIFF ( p , output_token . value ) ;
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 ,
2001-12-08 14:18:56 +03:00
& output_token , & conf_state ,
& input_token ) ;
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
free ( output_token . value ) ;
cred . bv_val = input_token . value ;
cred . bv_len = input_token . length ;
2001-12-19 15:21:12 +03:00
rc = ldap_sasl_bind_s ( ads - > ld , NULL , " GSSAPI " , & cred , NULL , NULL ,
2001-12-08 14:18:56 +03:00
& scred ) ;
2001-12-19 15:21:12 +03:00
status = ADS_ERROR ( rc ) ;
2001-12-08 14:18:56 +03:00
gss_release_buffer ( & minor_status , & input_token ) ;
failed :
2001-12-19 15:21:12 +03:00
return status ;
2001-12-08 14:18:56 +03:00
}
2002-09-25 19:19:00 +04:00
# endif
/* mapping between SASL mechanisms and functions */
static struct {
const char * name ;
ADS_STATUS ( * fn ) ( ADS_STRUCT * ) ;
} sasl_mechanisms [ ] = {
{ " GSS-SPNEGO " , ads_sasl_spnego_bind } ,
# ifdef HAVE_GSSAPI
{ " 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 ;
void * res ;
/* 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 ;
values = ldap_get_values ( ads - > ld , res , " supportedSASLMechanisms " ) ;
/* 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 ] ) ) ;
status = sasl_mechanisms [ i ] . fn ( ads ) ;
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
}
# endif