2021-12-08 16:05:17 +01:00
/*
2002-01-30 06:08:46 +00:00
Unix SMB / CIFS implementation .
2001-12-08 11:18:56 +00:00
ads sasl code
Copyright ( C ) Andrew Tridgell 2001
2021-12-08 16:05:17 +01:00
2001-12-08 11:18:56 +00:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-09 19:25:36 +00:00
the Free Software Foundation ; either version 3 of the License , or
2001-12-08 11:18:56 +00:00
( at your option ) any later version .
2021-12-08 16:05:17 +01:00
2001-12-08 11:18:56 +00:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2021-12-08 16:05:17 +01:00
2001-12-08 11:18:56 +00:00
You should have received a copy of the GNU General Public License
2007-07-10 00:52:41 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2001-12-08 11:18:56 +00:00
*/
# include "includes.h"
2009-09-17 00:21:01 +02:00
# include "../libcli/auth/spnego.h"
2015-12-09 15:02:29 +01:00
# include "auth/credentials/credentials.h"
2011-12-27 12:27:11 +11:00
# include "auth/gensec/gensec.h"
# include "auth_generic.h"
2010-07-02 00:32:52 +02:00
# include "ads.h"
# include "smb_krb5.h"
2012-04-24 19:37:13 +03:00
# include "system/gssapi.h"
2012-07-23 12:47:01 +10:00
# include "lib/param/loadparm.h"
2016-07-06 12:48:11 +02:00
# include "krb5_env.h"
2023-09-14 19:00:06 +02:00
# include "lib/util/asn1.h"
2001-12-08 11:18:56 +00:00
2002-10-01 18:26:00 +00:00
# ifdef HAVE_LDAP
2001-12-08 11:18:56 +00:00
2017-05-05 15:37:20 +03:00
static ADS_STATUS ads_sasl_gensec_wrap ( struct ads_saslwrap * wrap ,
uint8_t * buf , uint32_t len )
2007-07-18 08:15:42 +00:00
{
2011-12-27 12:27:11 +11:00
struct gensec_security * gensec_security =
2017-05-05 15:37:20 +03:00
talloc_get_type_abort ( wrap - > wrap_private_data ,
2011-12-27 12:27:11 +11:00
struct gensec_security ) ;
2007-07-18 08:15:42 +00:00
NTSTATUS nt_status ;
2011-12-27 12:27:11 +11: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 08:15:42 +00:00
}
2017-05-05 15:37:20 +03:00
if ( ( wrap - > out . size - 4 ) < wrapped . length ) {
2016-03-05 02:53:45 +01:00
TALLOC_FREE ( frame ) ;
2011-12-27 12:27:11 +11:00
return ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
}
2007-07-18 08:15:42 +00:00
2011-12-27 12:27:11 +11:00
/* copy the wrapped blob to the right location */
2017-05-05 15:37:20 +03:00
memcpy ( wrap - > out . buf + 4 , wrapped . data , wrapped . length ) ;
2007-07-18 08:15:42 +00:00
/* set how many bytes must be written to the underlying socket */
2017-05-05 15:37:20 +03:00
wrap - > out . left = 4 + wrapped . length ;
2011-12-27 12:27:11 +11:00
TALLOC_FREE ( frame ) ;
2007-07-18 08:15:42 +00:00
return ADS_SUCCESS ;
}
2017-05-05 15:37:20 +03:00
static ADS_STATUS ads_sasl_gensec_unwrap ( struct ads_saslwrap * wrap )
2007-07-18 08:15:42 +00:00
{
2011-12-27 12:27:11 +11:00
struct gensec_security * gensec_security =
2017-05-05 15:37:20 +03:00
talloc_get_type_abort ( wrap - > wrap_private_data ,
2011-12-27 12:27:11 +11:00
struct gensec_security ) ;
2007-07-18 08:15:42 +00:00
NTSTATUS nt_status ;
2011-12-27 12:27:11 +11:00
DATA_BLOB unwrapped , wrapped ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2017-05-05 15:37:20 +03:00
wrapped = data_blob_const ( wrap - > in . buf + 4 , wrap - > in . ofs - 4 ) ;
2011-12-27 12:27:11 +11:00
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 08:15:42 +00:00
}
2011-12-27 12:27:11 +11:00
/* copy the wrapped blob to the right location */
2017-05-05 15:37:20 +03:00
memcpy ( wrap - > in . buf + 4 , unwrapped . data , unwrapped . length ) ;
2011-12-27 12:27:11 +11:00
/* set how many bytes must be written to the underlying socket */
2017-05-05 15:37:20 +03:00
wrap - > in . left = unwrapped . length ;
wrap - > in . ofs = 4 ;
2011-12-27 12:27:11 +11:00
TALLOC_FREE ( frame ) ;
2007-07-18 08:15:42 +00:00
return ADS_SUCCESS ;
}
2017-05-05 15:37:20 +03:00
static void ads_sasl_gensec_disconnect ( struct ads_saslwrap * wrap )
2007-07-18 08:15:42 +00:00
{
2011-12-27 12:27:11 +11:00
struct gensec_security * gensec_security =
2017-05-05 15:37:20 +03:00
talloc_get_type_abort ( wrap - > wrap_private_data ,
2011-12-27 12:27:11 +11:00
struct gensec_security ) ;
2007-07-18 08:15:42 +00:00
2011-12-27 12:27:11 +11:00
TALLOC_FREE ( gensec_security ) ;
2007-07-18 08:15:42 +00:00
2017-05-05 15:37:20 +03:00
wrap - > wrap_ops = NULL ;
wrap - > wrap_private_data = NULL ;
2007-07-18 08:15:42 +00:00
}
2015-12-09 13:14:05 +01:00
static const struct ads_saslwrap_ops ads_sasl_gensec_ops = {
. name = " gensec " ,
. wrap = ads_sasl_gensec_wrap ,
. unwrap = ads_sasl_gensec_unwrap ,
. disconnect = ads_sasl_gensec_disconnect
2007-07-18 08:15:42 +00:00
} ;
2021-12-08 16:05:17 +01:00
/*
2015-12-09 13:14:05 +01:00
perform a LDAP / SASL / SPNEGO / { NTLMSSP , KRB5 } bind ( just how many layers can
2002-09-25 15:19:00 +00:00
we fit on one socket ? ? )
2001-12-08 11:18:56 +00:00
*/
2015-12-09 13:14:05 +01:00
static ADS_STATUS ads_sasl_spnego_gensec_bind ( ADS_STRUCT * ads ,
const char * sasl ,
enum credentials_use_kerberos krb5_state ,
const char * target_service ,
const char * target_hostname ,
const DATA_BLOB server_blob )
2001-12-08 11:18:56 +00:00
{
2007-05-14 12:16:20 +00:00
DATA_BLOB blob_in = data_blob_null ;
DATA_BLOB blob_out = data_blob_null ;
2002-09-25 15:19:00 +00:00
int rc ;
2005-11-04 17:39:42 +00:00
NTSTATUS nt_status ;
2007-07-31 12:30:37 +00:00
ADS_STATUS status ;
2011-12-27 12:27:11 +11:00
struct auth_generic_state * auth_generic_state ;
2015-12-09 13:14:05 +01:00
bool use_spnego_principal = lp_client_use_spnego_principal ( ) ;
const char * sasl_list [ ] = { sasl , NULL } ;
2016-04-18 23:08:38 +03:00
NTTIME end_nt_time ;
2017-05-05 15:37:20 +03:00
struct ads_saslwrap * wrap = & ads - > ldap_wrap_data ;
2002-09-25 15:19:00 +00:00
2011-12-27 12:27:11 +11:00
nt_status = auth_generic_client_prepare ( NULL , & auth_generic_state ) ;
2009-12-30 14:13:45 +01:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2005-11-04 17:39:42 +00:00
return ADS_ERROR_NT ( nt_status ) ;
2002-09-25 15:19:00 +00:00
}
2011-12-27 12:27:11 +11:00
if ( ! NT_STATUS_IS_OK ( nt_status = auth_generic_set_username ( auth_generic_state , ads - > auth . user_name ) ) ) {
2005-11-04 17:39:42 +00:00
return ADS_ERROR_NT ( nt_status ) ;
}
2011-12-27 12:27:11 +11:00
if ( ! NT_STATUS_IS_OK ( nt_status = auth_generic_set_domain ( auth_generic_state , ads - > auth . realm ) ) ) {
2005-11-04 17:39:42 +00:00
return ADS_ERROR_NT ( nt_status ) ;
}
2011-12-27 12:27:11 +11:00
if ( ! NT_STATUS_IS_OK ( nt_status = auth_generic_set_password ( auth_generic_state , ads - > auth . password ) ) ) {
2005-11-04 17:39:42 +00:00
return ADS_ERROR_NT ( nt_status ) ;
2002-09-25 15:19:00 +00:00
}
2015-12-09 13:14:05 +01:00
if ( server_blob . length = = 0 ) {
use_spnego_principal = false ;
}
2020-08-20 09:40:41 +02:00
if ( krb5_state = = CRED_USE_KERBEROS_DISABLED ) {
2015-12-09 13:14:05 +01:00
use_spnego_principal = false ;
}
2015-12-09 15:02:29 +01:00
cli_credentials_set_kerberos_state ( auth_generic_state - > credentials ,
2020-08-19 15:46:11 +02:00
krb5_state ,
CRED_SPECIFIED ) ;
2015-12-09 13:14:05 +01:00
if ( target_service ! = NULL ) {
nt_status = gensec_set_target_service (
auth_generic_state - > gensec_security ,
target_service ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return ADS_ERROR_NT ( nt_status ) ;
}
}
if ( target_hostname ! = NULL ) {
nt_status = gensec_set_target_hostname (
auth_generic_state - > gensec_security ,
target_hostname ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return ADS_ERROR_NT ( nt_status ) ;
}
}
if ( target_service ! = NULL & & target_hostname ! = NULL ) {
use_spnego_principal = false ;
}
2015-12-09 15:02:29 +01:00
2017-05-05 15:37:20 +03:00
switch ( wrap - > wrap_type ) {
2007-07-18 08:15:42 +00:00
case ADS_SASLWRAP_TYPE_SEAL :
2011-12-27 12:27:11 +11: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 08:15:42 +00:00
break ;
case ADS_SASLWRAP_TYPE_SIGN :
if ( ads - > auth . flags & ADS_AUTH_SASL_FORCE ) {
2011-12-27 12:27:11 +11:00
gensec_want_feature ( auth_generic_state - > gensec_security , GENSEC_FEATURE_SIGN ) ;
2007-07-18 08:15:42 +00:00
} else {
/*
* windows servers are broken with sign only ,
2015-12-09 15:04:02 +01:00
* so we let the NTLMSSP backend to seal here ,
* via GENSEC_FEATURE_LDAP_STYLE .
2007-07-18 08:15:42 +00:00
*/
2011-12-27 12:27:11 +11:00
gensec_want_feature ( auth_generic_state - > gensec_security , GENSEC_FEATURE_SIGN ) ;
2015-12-09 15:04:02 +01:00
gensec_want_feature ( auth_generic_state - > gensec_security , GENSEC_FEATURE_LDAP_STYLE ) ;
2007-07-18 08:15:42 +00:00
}
break ;
case ADS_SASLWRAP_TYPE_PLAIN :
break ;
}
2015-12-09 13:14:05 +01:00
nt_status = auth_generic_client_start_by_sasl ( auth_generic_state ,
sasl_list ) ;
2011-12-27 12:27:11 +11:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return ADS_ERROR_NT ( nt_status ) ;
}
2007-07-18 08:15:42 +00:00
2015-12-09 15:02:29 +01:00
rc = LDAP_SASL_BIND_IN_PROGRESS ;
2015-12-09 13:14:05 +01:00
if ( use_spnego_principal ) {
blob_in = data_blob_dup_talloc ( talloc_tos ( ) , server_blob ) ;
if ( blob_in . length = = 0 ) {
TALLOC_FREE ( auth_generic_state ) ;
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
} else {
blob_in = data_blob_null ;
}
2015-12-09 15:02:29 +01:00
blob_out = data_blob_null ;
while ( true ) {
struct berval cred , * scred = NULL ;
2005-11-04 17:39:42 +00:00
2011-12-27 12:27:11 +11:00
nt_status = gensec_update ( auth_generic_state - > gensec_security ,
2013-12-13 19:56:13 +01:00
talloc_tos ( ) , blob_in , & blob_out ) ;
2005-11-04 17:39:42 +00:00
data_blob_free ( & blob_in ) ;
2015-12-09 15:02:29 +01:00
if ( ! NT_STATUS_EQUAL ( nt_status , NT_STATUS_MORE_PROCESSING_REQUIRED )
& & ! NT_STATUS_IS_OK ( nt_status ) )
{
TALLOC_FREE ( auth_generic_state ) ;
2005-11-04 17:39:42 +00:00
data_blob_free ( & blob_out ) ;
2015-12-09 15:02:29 +01:00
return ADS_ERROR_NT ( nt_status ) ;
}
2005-11-04 17:39:42 +00:00
2015-12-09 15:02:29 +01:00
if ( NT_STATUS_IS_OK ( nt_status ) & & rc = = 0 & & blob_out . length = = 0 ) {
break ;
}
2005-11-04 17:39:42 +00:00
2015-12-09 15:02:29 +01:00
cred . bv_val = ( char * ) blob_out . data ;
cred . bv_len = blob_out . length ;
scred = NULL ;
2015-12-09 13:14:05 +01:00
rc = ldap_sasl_bind_s ( ads - > ldap . ld , NULL , sasl , & cred , NULL , NULL , & scred ) ;
2015-12-09 15:02:29 +01:00
data_blob_free ( & blob_out ) ;
if ( ( rc ! = LDAP_SASL_BIND_IN_PROGRESS ) & & ( rc ! = 0 ) ) {
2005-11-04 17:39:42 +00:00
if ( scred ) {
ber_bvfree ( scred ) ;
}
2011-12-27 12:27:11 +11:00
TALLOC_FREE ( auth_generic_state ) ;
2015-12-09 15:02:29 +01:00
return ADS_ERROR ( rc ) ;
2005-11-04 17:39:42 +00:00
}
2015-12-09 15:02:29 +01:00
if ( scred ) {
blob_in = data_blob_talloc ( talloc_tos ( ) ,
scred - > bv_val ,
scred - > bv_len ) ;
if ( blob_in . length ! = scred - > bv_len ) {
ber_bvfree ( scred ) ;
2011-12-27 12:27:11 +11:00
TALLOC_FREE ( auth_generic_state ) ;
2015-12-09 15:02:29 +01:00
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
2005-11-04 17:39:42 +00:00
}
2015-12-09 15:02:29 +01:00
ber_bvfree ( scred ) ;
} else {
blob_in = data_blob_null ;
2005-11-04 17:39:42 +00:00
}
2015-12-09 15:02:29 +01:00
if ( NT_STATUS_IS_OK ( nt_status ) & & rc = = 0 & & blob_in . length = = 0 ) {
break ;
}
}
data_blob_free ( & blob_in ) ;
data_blob_free ( & blob_out ) ;
2017-05-05 15:37:20 +03:00
if ( wrap - > wrap_type > = ADS_SASLWRAP_TYPE_SEAL ) {
2016-03-24 15:50:49 +01:00
bool ok ;
ok = gensec_have_feature ( auth_generic_state - > gensec_security ,
GENSEC_FEATURE_SEAL ) ;
if ( ! ok ) {
DEBUG ( 0 , ( " The gensec feature sealing request, but unavailable \n " ) ) ;
TALLOC_FREE ( auth_generic_state ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
}
ok = gensec_have_feature ( auth_generic_state - > gensec_security ,
GENSEC_FEATURE_SIGN ) ;
if ( ! ok ) {
DEBUG ( 0 , ( " The gensec feature signing request, but unavailable \n " ) ) ;
TALLOC_FREE ( auth_generic_state ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
}
2017-05-05 15:37:20 +03:00
} else if ( wrap - > wrap_type > = ADS_SASLWRAP_TYPE_SIGN ) {
2016-03-24 15:50:49 +01:00
bool ok ;
ok = gensec_have_feature ( auth_generic_state - > gensec_security ,
GENSEC_FEATURE_SIGN ) ;
if ( ! ok ) {
DEBUG ( 0 , ( " The gensec feature signing request, but unavailable \n " ) ) ;
TALLOC_FREE ( auth_generic_state ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
}
}
2016-04-18 23:08:38 +03:00
ads - > auth . tgs_expire = LONG_MAX ;
end_nt_time = gensec_expire_time ( auth_generic_state - > gensec_security ) ;
if ( end_nt_time ! = GENSEC_EXPIRE_TIME_INFINITY ) {
struct timeval tv ;
nttime_to_timeval ( & tv , end_nt_time ) ;
ads - > auth . tgs_expire = tv . tv_sec ;
}
2017-05-05 15:37:20 +03:00
if ( wrap - > wrap_type > ADS_SASLWRAP_TYPE_PLAIN ) {
size_t max_wrapped =
gensec_max_wrapped_size ( auth_generic_state - > gensec_security ) ;
wrap - > out . max_unwrapped =
gensec_max_input_size ( auth_generic_state - > gensec_security ) ;
2015-06-19 01:07:49 +02:00
2017-05-05 15:37:20 +03:00
wrap - > out . sig_size = max_wrapped - wrap - > out . max_unwrapped ;
2016-04-08 10:05:38 +02:00
/*
* Note that we have to truncate this to 0x2C
* ( taken from a capture with LDAP unbind ) , as the
* signature size is not constant for Kerberos with
* arcfour - hmac - md5 .
*/
2017-05-05 15:37:20 +03:00
wrap - > in . min_wrapped = MIN ( wrap - > out . sig_size , 0x2C ) ;
wrap - > in . max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED ;
status = ads_setup_sasl_wrapping ( wrap , ads - > ldap . ld ,
& ads_sasl_gensec_ops ,
auth_generic_state - > gensec_security ) ;
2007-07-31 12:27:25 +00:00
if ( ! ADS_ERR_OK ( status ) ) {
2007-07-31 12:30:37 +00:00
DEBUG ( 0 , ( " ads_setup_sasl_wrapping() failed: %s \n " ,
2007-07-31 12:27:25 +00:00
ads_errstr ( status ) ) ) ;
2011-12-27 12:27:11 +11:00
TALLOC_FREE ( auth_generic_state ) ;
2007-07-31 12:27:25 +00:00
return status ;
}
2011-12-27 12:27:11 +11:00
/* Only keep the gensec_security element around long-term */
talloc_steal ( NULL , auth_generic_state - > gensec_security ) ;
2007-07-18 08:15:42 +00:00
}
2011-12-27 12:27:11 +11:00
TALLOC_FREE ( auth_generic_state ) ;
2004-05-06 23:08:56 +00:00
2005-11-04 17:39:42 +00:00
return ADS_ERROR ( rc ) ;
2001-12-08 11:18:56 +00:00
}
2002-09-25 15:19:00 +00:00
2006-12-12 20:27:01 +00:00
# ifdef HAVE_KRB5
2007-07-31 09:31:47 +00:00
struct ads_service_principal {
2016-03-02 11:33:04 +01:00
char * service ;
char * hostname ;
char * string ;
2007-07-31 09:31:47 +00:00
} ;
static void ads_free_service_principal ( struct ads_service_principal * p )
{
2016-03-02 11:33:04 +01:00
SAFE_FREE ( p - > service ) ;
SAFE_FREE ( p - > hostname ) ;
2007-07-31 09:31:47 +00:00
SAFE_FREE ( p - > string ) ;
ZERO_STRUCTP ( p ) ;
}
2016-03-02 11:33:04 +01:00
static ADS_STATUS ads_guess_target ( ADS_STRUCT * ads ,
char * * service ,
char * * hostname ,
char * * principal )
2011-02-11 11:14:27 +01:00
{
2014-09-23 14:09:41 +02:00
ADS_STATUS status = ADS_ERROR ( LDAP_NO_MEMORY ) ;
2011-02-11 11:14:27 +01:00
char * princ = NULL ;
2014-09-23 14:09:41 +02:00
TALLOC_CTX * frame ;
char * server = NULL ;
char * realm = NULL ;
int rc ;
2011-02-11 11:14:27 +01:00
2014-09-23 14:09:41 +02:00
frame = talloc_stackframe ( ) ;
if ( frame = = NULL ) {
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
2011-02-11 11:14:27 +01:00
2014-09-23 14:09:41 +02:00
if ( ads - > server . realm & & ads - > server . ldap_server ) {
server = strlower_talloc ( frame , ads - > server . ldap_server ) ;
if ( server = = NULL ) {
goto out ;
2012-08-08 17:01:00 -07:00
}
2014-09-23 14:09:41 +02:00
realm = strupper_talloc ( frame , ads - > server . realm ) ;
if ( realm = = NULL ) {
goto out ;
2012-08-08 15:35:28 -07:00
}
2014-09-23 14:09:41 +02:00
/*
* If we got a name which is bigger than a NetBIOS name ,
* but isn ' t a FQDN , create one .
*/
if ( strlen ( server ) > 15 & & strstr ( server , " . " ) = = NULL ) {
char * dnsdomain ;
2011-02-11 11:14:27 +01:00
2014-09-23 14:09:41 +02:00
dnsdomain = strlower_talloc ( frame , ads - > server . realm ) ;
if ( dnsdomain = = NULL ) {
goto out ;
}
2011-02-11 11:14:27 +01:00
2014-09-23 14:09:41 +02:00
server = talloc_asprintf ( frame ,
" %s.%s " ,
server , dnsdomain ) ;
if ( server = = NULL ) {
goto out ;
}
2011-02-11 11:14:27 +01:00
}
} else if ( ads - > config . realm & & ads - > config . ldap_server_name ) {
2014-09-23 14:09:41 +02:00
server = strlower_talloc ( frame , ads - > config . ldap_server_name ) ;
if ( server = = NULL ) {
goto out ;
2011-02-11 11:14:27 +01:00
}
2014-09-23 14:09:41 +02:00
realm = strupper_talloc ( frame , ads - > config . realm ) ;
if ( realm = = NULL ) {
goto out ;
2012-08-08 17:01:00 -07:00
}
2014-09-23 14:09:41 +02:00
/*
* If we got a name which is bigger than a NetBIOS name ,
* but isn ' t a FQDN , create one .
*/
if ( strlen ( server ) > 15 & & strstr ( server , " . " ) = = NULL ) {
char * dnsdomain ;
2011-02-11 11:14:27 +01:00
2014-09-23 14:09:41 +02:00
dnsdomain = strlower_talloc ( frame , ads - > server . realm ) ;
if ( dnsdomain = = NULL ) {
goto out ;
}
2011-02-11 11:14:27 +01:00
2014-09-23 14:09:41 +02:00
server = talloc_asprintf ( frame ,
" %s.%s " ,
server , dnsdomain ) ;
if ( server = = NULL ) {
goto out ;
}
2011-02-11 11:14:27 +01:00
}
}
2014-09-23 14:09:41 +02:00
if ( server = = NULL | | realm = = NULL ) {
goto out ;
}
2016-03-02 11:33:04 +01:00
* service = SMB_STRDUP ( " ldap " ) ;
if ( * service = = NULL ) {
status = ADS_ERROR ( LDAP_PARAM_ERROR ) ;
goto out ;
}
* hostname = SMB_STRDUP ( server ) ;
if ( * hostname = = NULL ) {
SAFE_FREE ( * service ) ;
status = ADS_ERROR ( LDAP_PARAM_ERROR ) ;
goto out ;
}
2014-09-23 14:09:41 +02:00
rc = asprintf ( & princ , " ldap/%s@%s " , server , realm ) ;
if ( rc = = - 1 | | princ = = NULL ) {
2016-03-02 11:33:04 +01:00
SAFE_FREE ( * service ) ;
SAFE_FREE ( * hostname ) ;
2014-09-23 14:09:41 +02:00
status = ADS_ERROR ( LDAP_PARAM_ERROR ) ;
goto out ;
2011-02-11 11:14:27 +01:00
}
2016-03-02 11:33:04 +01:00
* principal = princ ;
2011-02-11 11:14:27 +01:00
2014-09-23 14:09:41 +02:00
status = ADS_SUCCESS ;
out :
TALLOC_FREE ( frame ) ;
return status ;
2011-02-11 11:14:27 +01:00
}
2007-07-31 09:31:47 +00:00
static ADS_STATUS ads_generate_service_principal ( ADS_STRUCT * ads ,
struct ads_service_principal * p )
{
ADS_STATUS status ;
ZERO_STRUCTP ( p ) ;
2016-03-02 11:33:04 +01:00
status = ads_guess_target ( ads ,
& p - > service ,
& p - > hostname ,
& p - > string ) ;
2016-03-02 11:31:01 +01:00
if ( ! ADS_ERR_OK ( status ) ) {
return status ;
2007-07-31 09:31:47 +00:00
}
2007-09-13 15:59:46 +00:00
return ADS_SUCCESS ;
2007-07-31 09:31:47 +00:00
}
2007-09-11 23:21:50 +00:00
# endif /* HAVE_KRB5 */
2002-09-25 15:19:00 +00:00
2023-09-14 19:00:06 +02:00
/*
parse a negTokenInit packet giving a GUID , a list of supported
OIDs ( the mechanisms ) and a principal name string
*/
static bool spnego_parse_negTokenInit ( TALLOC_CTX * ctx ,
DATA_BLOB blob ,
char * OIDs [ ASN1_MAX_OIDS ] ,
char * * principal ,
DATA_BLOB * secblob )
{
int i ;
bool ret = false ;
ASN1_DATA * data ;
for ( i = 0 ; i < ASN1_MAX_OIDS ; i + + ) {
OIDs [ i ] = NULL ;
}
if ( principal ) {
* principal = NULL ;
}
if ( secblob ) {
* secblob = data_blob_null ;
}
data = asn1_init ( talloc_tos ( ) , ASN1_MAX_TREE_DEPTH ) ;
if ( data = = NULL ) {
return false ;
}
if ( ! asn1_load ( data , blob ) ) goto err ;
if ( ! asn1_start_tag ( data , ASN1_APPLICATION ( 0 ) ) ) goto err ;
if ( ! asn1_check_OID ( data , OID_SPNEGO ) ) goto err ;
/* negTokenInit [0] NegTokenInit */
if ( ! asn1_start_tag ( data , ASN1_CONTEXT ( 0 ) ) ) goto err ;
if ( ! asn1_start_tag ( data , ASN1_SEQUENCE ( 0 ) ) ) goto err ;
/* mechTypes [0] MechTypeList OPTIONAL */
/* Not really optional, we depend on this to decide
* what mechanisms we have to work with . */
if ( ! asn1_start_tag ( data , ASN1_CONTEXT ( 0 ) ) ) goto err ;
if ( ! asn1_start_tag ( data , ASN1_SEQUENCE ( 0 ) ) ) goto err ;
for ( i = 0 ; asn1_tag_remaining ( data ) > 0 & & i < ASN1_MAX_OIDS - 1 ; i + + ) {
if ( ! asn1_read_OID ( data , ctx , & OIDs [ i ] ) ) {
goto err ;
}
if ( asn1_has_error ( data ) ) {
goto err ;
}
}
OIDs [ i ] = NULL ;
if ( ! asn1_end_tag ( data ) ) goto err ;
if ( ! asn1_end_tag ( data ) ) goto err ;
/*
Win7 + Live Sign - in Assistant attaches a mechToken
ASN1_CONTEXT ( 2 ) to the negTokenInit packet
which breaks our negotiation if we just assume
the next tag is ASN1_CONTEXT ( 3 ) .
*/
if ( asn1_peek_tag ( data , ASN1_CONTEXT ( 1 ) ) ) {
uint8_t flags ;
/* reqFlags [1] ContextFlags OPTIONAL */
if ( ! asn1_start_tag ( data , ASN1_CONTEXT ( 1 ) ) ) goto err ;
if ( ! asn1_start_tag ( data , ASN1_BIT_STRING ) ) goto err ;
while ( asn1_tag_remaining ( data ) > 0 ) {
if ( ! asn1_read_uint8 ( data , & flags ) ) goto err ;
}
if ( ! asn1_end_tag ( data ) ) goto err ;
if ( ! asn1_end_tag ( data ) ) goto err ;
}
if ( asn1_peek_tag ( data , ASN1_CONTEXT ( 2 ) ) ) {
DATA_BLOB sblob = data_blob_null ;
/* mechToken [2] OCTET STRING OPTIONAL */
if ( ! asn1_start_tag ( data , ASN1_CONTEXT ( 2 ) ) ) goto err ;
if ( ! asn1_read_OctetString ( data , ctx , & sblob ) ) goto err ;
if ( ! asn1_end_tag ( data ) ) {
data_blob_free ( & sblob ) ;
goto err ;
}
if ( secblob ) {
* secblob = sblob ;
} else {
data_blob_free ( & sblob ) ;
}
}
if ( asn1_peek_tag ( data , ASN1_CONTEXT ( 3 ) ) ) {
char * princ = NULL ;
/* mechListMIC [3] OCTET STRING OPTIONAL */
if ( ! asn1_start_tag ( data , ASN1_CONTEXT ( 3 ) ) ) goto err ;
if ( ! asn1_start_tag ( data , ASN1_SEQUENCE ( 0 ) ) ) goto err ;
if ( ! asn1_start_tag ( data , ASN1_CONTEXT ( 0 ) ) ) goto err ;
if ( ! asn1_read_GeneralString ( data , ctx , & princ ) ) goto err ;
if ( ! asn1_end_tag ( data ) ) goto err ;
if ( ! asn1_end_tag ( data ) ) goto err ;
if ( ! asn1_end_tag ( data ) ) goto err ;
if ( principal ) {
* principal = princ ;
} else {
TALLOC_FREE ( princ ) ;
}
}
if ( ! asn1_end_tag ( data ) ) goto err ;
if ( ! asn1_end_tag ( data ) ) goto err ;
if ( ! asn1_end_tag ( data ) ) goto err ;
ret = ! asn1_has_error ( data ) ;
err :
if ( asn1_has_error ( data ) ) {
int j ;
if ( principal ) {
TALLOC_FREE ( * principal ) ;
}
if ( secblob ) {
data_blob_free ( secblob ) ;
}
for ( j = 0 ; j < i & & j < ASN1_MAX_OIDS - 1 ; j + + ) {
TALLOC_FREE ( OIDs [ j ] ) ;
}
}
asn1_free ( data ) ;
return ret ;
}
2021-12-08 16:05:17 +01:00
/*
2002-09-25 15:19:00 +00:00
this performs a SASL / SPNEGO bind
*/
static ADS_STATUS ads_sasl_spnego_bind ( ADS_STRUCT * ads )
{
2016-03-02 11:42:51 +01:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2016-03-15 20:34:27 +01:00
struct ads_service_principal p = { 0 } ;
2002-09-25 15:19:00 +00:00
struct berval * scred = NULL ;
int rc , i ;
ADS_STATUS status ;
2016-07-03 22:51:56 +03:00
DATA_BLOB blob = data_blob_null ;
2007-07-31 09:31:47 +00:00
char * given_principal = NULL ;
2002-09-25 15:19:00 +00:00
char * OIDs [ ASN1_MAX_OIDS ] ;
2005-03-22 15:12:50 +00:00
# ifdef HAVE_KRB5
2007-10-18 17:40:25 -07:00
bool got_kerberos_mechanism = False ;
2005-03-22 15:12:50 +00:00
# endif
2017-02-23 11:54:21 +01:00
const char * mech = NULL ;
2002-09-25 15:19:00 +00:00
2007-07-16 11:08:00 +00:00
rc = ldap_sasl_bind_s ( ads - > ldap . ld , NULL , " GSS-SPNEGO " , NULL , NULL , NULL , & scred ) ;
2002-09-25 15:19:00 +00:00
if ( rc ! = LDAP_SASL_BIND_IN_PROGRESS ) {
status = ADS_ERROR ( rc ) ;
2016-03-02 11:42:51 +01:00
goto done ;
2002-09-25 15:19:00 +00:00
}
blob = data_blob ( scred - > bv_val , scred - > bv_len ) ;
2004-01-08 08:19:18 +00:00
ber_bvfree ( scred ) ;
2002-09-25 15:19:00 +00:00
#if 0
file_save ( " sasl_spnego.dat " , blob . data , blob . length ) ;
2001-12-08 11:18:56 +00:00
# endif
2021-12-08 16:05:17 +01:00
/* the server sent us the first part of the SPNEGO exchange in the negprot
2002-09-25 15:19:00 +00:00
reply */
2010-09-23 21:44:24 -07:00
if ( ! spnego_parse_negTokenInit ( talloc_tos ( ) , blob , OIDs , & given_principal , NULL ) | |
OIDs [ 0 ] = = NULL ) {
2002-09-25 15:19:00 +00:00
status = ADS_ERROR ( LDAP_OPERATIONS_ERROR ) ;
2016-03-02 11:42:51 +01:00
goto done ;
2002-09-25 15:19:00 +00:00
}
2016-03-02 11:31:01 +01:00
TALLOC_FREE ( given_principal ) ;
2002-09-25 15:19:00 +00:00
/* make sure the server understands kerberos */
for ( i = 0 ; OIDs [ i ] ; i + + ) {
2004-07-07 18:12:09 +00:00
DEBUG ( 3 , ( " ads_sasl_spnego_bind: got OID=%s \n " , OIDs [ i ] ) ) ;
2005-03-22 15:12:50 +00:00
# ifdef HAVE_KRB5
2002-09-25 15:19:00 +00:00
if ( strcmp ( OIDs [ i ] , OID_KERBEROS5_OLD ) = = 0 | |
strcmp ( OIDs [ i ] , OID_KERBEROS5 ) = = 0 ) {
got_kerberos_mechanism = True ;
}
2005-03-22 15:12:50 +00:00
# endif
2008-10-22 14:06:08 +02:00
talloc_free ( OIDs [ i ] ) ;
2002-09-25 15:19:00 +00:00
}
2001-12-08 11:18:56 +00:00
2016-03-02 11:42:51 +01:00
status = ads_generate_service_principal ( ads , & p ) ;
if ( ! ADS_ERR_OK ( status ) ) {
goto done ;
}
2002-10-01 18:26:00 +00:00
# ifdef HAVE_KRB5
2002-09-25 15:19:00 +00:00
if ( ! ( ads - > auth . flags & ADS_AUTH_DISABLE_KERBEROS ) & &
2021-12-08 16:05:17 +01:00
got_kerberos_mechanism )
2007-02-10 20:29:09 +00:00
{
2017-02-23 11:54:21 +01:00
mech = " KRB5 " ;
2016-07-06 12:44:11 +02:00
if ( ads - > auth . password = = NULL | |
ads - > auth . password [ 0 ] = = ' \0 ' )
{
status = ads_sasl_spnego_gensec_bind ( ads , " GSS-SPNEGO " ,
2020-08-20 09:40:41 +02:00
CRED_USE_KERBEROS_REQUIRED ,
2016-07-06 12:44:11 +02:00
p . service , p . hostname ,
blob ) ;
if ( ADS_ERR_OK ( status ) ) {
ads_free_service_principal ( & p ) ;
goto done ;
}
DEBUG ( 10 , ( " ads_sasl_spnego_gensec_bind(KRB5) failed with: %s, "
" calling kinit \n " , ads_errstr ( status ) ) ) ;
}
2007-02-08 17:02:39 +00:00
2021-12-08 16:05:17 +01:00
status = ADS_ERROR_KRB5 ( ads_kinit_password ( ads ) ) ;
2004-01-08 08:19:18 +00:00
if ( ADS_ERR_OK ( status ) ) {
2016-03-02 11:42:51 +01:00
status = ads_sasl_spnego_gensec_bind ( ads , " GSS-SPNEGO " ,
2020-08-20 09:40:41 +02:00
CRED_USE_KERBEROS_REQUIRED ,
2016-03-02 11:42:51 +01:00
p . service , p . hostname ,
blob ) ;
2007-09-26 01:02:52 +00:00
if ( ! ADS_ERR_OK ( status ) ) {
2022-01-07 10:31:19 +01:00
DBG_ERR ( " kinit succeeded but "
" SPNEGO bind with Kerberos failed "
" for %s/%s - user[%s], realm[%s]: %s \n " ,
2017-02-23 11:54:21 +01:00
p . service , p . hostname ,
ads - > auth . user_name ,
ads - > auth . realm ,
2022-01-07 10:31:19 +01:00
ads_errstr ( status ) ) ;
2007-09-26 01:02:52 +00:00
}
2003-04-24 14:07:13 +00:00
}
2004-01-08 08:19:18 +00:00
2003-06-10 03:47:42 +00:00
/* only fallback to NTLMSSP if allowed */
2021-12-08 16:05:17 +01:00
if ( ADS_ERR_OK ( status ) | |
2003-06-10 03:47:42 +00:00
! ( ads - > auth . flags & ADS_AUTH_ALLOW_NTLMSSP ) ) {
2016-03-02 11:42:51 +01:00
goto done ;
2003-06-10 03:47:42 +00:00
}
2017-02-23 11:54:21 +01:00
2022-01-07 10:31:19 +01:00
DBG_WARNING ( " SASL bind with Kerberos failed "
" for %s/%s - user[%s], realm[%s]: %s, "
" try to fallback to NTLMSSP \n " ,
p . service , p . hostname ,
ads - > auth . user_name ,
ads - > auth . realm ,
ads_errstr ( status ) ) ;
2007-07-31 09:31:47 +00:00
}
2016-03-02 11:31:01 +01:00
# endif
2004-05-06 23:08:56 +00:00
2002-09-25 15:19:00 +00:00
/* lets do NTLMSSP ... this has the big advantage that we don't need
2021-12-08 16:05:17 +01:00
to sync clocks , and we don ' t rely on special versions of the krb5
2002-09-25 15:19:00 +00:00
library for HMAC_MD4 encryption */
2017-02-23 11:54:21 +01:00
mech = " NTLMSSP " ;
2021-12-09 13:43:08 +01:00
2022-01-03 11:13:06 +01:00
if ( ! ( ads - > auth . flags & ADS_AUTH_ALLOW_NTLMSSP ) ) {
DBG_WARNING ( " We can't use NTLMSSP, it is not allowed. \n " ) ;
status = ADS_ERROR_NT ( NT_STATUS_NETWORK_CREDENTIAL_CONFLICT ) ;
goto done ;
}
2021-12-09 13:43:08 +01:00
if ( lp_weak_crypto ( ) = = SAMBA_WEAK_CRYPTO_DISALLOWED ) {
DBG_WARNING ( " We can't fallback to NTLMSSP, weak crypto is "
" disallowed. \n " ) ;
status = ADS_ERROR_NT ( NT_STATUS_NETWORK_CREDENTIAL_CONFLICT ) ;
goto done ;
}
2016-03-02 11:42:51 +01:00
status = ads_sasl_spnego_gensec_bind ( ads , " GSS-SPNEGO " ,
2020-08-20 09:40:41 +02:00
CRED_USE_KERBEROS_DISABLED ,
2016-03-02 11:42:51 +01:00
p . service , p . hostname ,
data_blob_null ) ;
done :
2017-02-23 11:54:21 +01:00
if ( ! ADS_ERR_OK ( status ) ) {
DEBUG ( 1 , ( " ads_sasl_spnego_gensec_bind(%s) failed "
" for %s/%s with user[%s] realm=[%s]: %s \n " , mech ,
p . service , p . hostname ,
ads - > auth . user_name ,
ads - > auth . realm ,
ads_errstr ( status ) ) ) ;
}
2016-03-02 11:42:51 +01:00
ads_free_service_principal ( & p ) ;
TALLOC_FREE ( frame ) ;
2016-07-03 22:51:56 +03:00
if ( blob . data ! = NULL ) {
data_blob_free ( & blob ) ;
}
2002-09-25 15:19:00 +00:00
return status ;
}
/* mapping between SASL mechanisms and functions */
static struct {
const char * name ;
ADS_STATUS ( * fn ) ( ADS_STRUCT * ) ;
} sasl_mechanisms [ ] = {
{ " GSS-SPNEGO " , ads_sasl_spnego_bind } ,
{ NULL , NULL }
} ;
2001-12-08 11:18:56 +00:00
2001-12-19 12:21:12 +00:00
ADS_STATUS ads_sasl_bind ( ADS_STRUCT * ads )
2001-12-08 11:18:56 +00:00
{
2002-09-25 15:19:00 +00:00
const char * attrs [ ] = { " supportedSASLMechanisms " , NULL } ;
char * * values ;
ADS_STATUS status ;
int i , j ;
2006-09-03 21:07:16 +00:00
LDAPMessage * res ;
2017-05-05 15:37:20 +03:00
struct ads_saslwrap * wrap = & ads - > ldap_wrap_data ;
2002-09-25 15:19:00 +00: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 11:08:00 +00:00
values = ldap_get_values ( ads - > ldap . ld , res , " supportedSASLMechanisms " ) ;
2002-09-25 15:19:00 +00:00
2007-07-18 07:45:16 +00:00
if ( ads - > auth . flags & ADS_AUTH_SASL_SEAL ) {
2017-05-05 15:37:20 +03:00
wrap - > wrap_type = ADS_SASLWRAP_TYPE_SEAL ;
2007-07-18 07:45:16 +00:00
} else if ( ads - > auth . flags & ADS_AUTH_SASL_SIGN ) {
2017-05-05 15:37:20 +03:00
wrap - > wrap_type = ADS_SASLWRAP_TYPE_SIGN ;
2007-07-18 07:45:16 +00:00
} else {
2017-05-05 15:37:20 +03:00
wrap - > wrap_type = ADS_SASLWRAP_TYPE_PLAIN ;
2007-07-18 07:45:16 +00:00
}
2002-09-25 15:19:00 +00: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 09:50:09 +02:00
retry :
2002-09-25 15:19:00 +00:00
status = sasl_mechanisms [ i ] . fn ( ads ) ;
2010-03-30 09:50:09 +02:00
if ( status . error_type = = ENUM_ADS_ERROR_LDAP & &
status . err . rc = = LDAP_STRONG_AUTH_REQUIRED & &
2017-05-05 15:37:20 +03:00
wrap - > wrap_type = = ADS_SASLWRAP_TYPE_PLAIN )
2010-03-30 09:50:09 +02:00
{
DEBUG ( 3 , ( " SASL bin got LDAP_STRONG_AUTH_REQUIRED "
" retrying with signing enabled \n " ) ) ;
2017-05-05 15:37:20 +03:00
wrap - > wrap_type = ADS_SASLWRAP_TYPE_SIGN ;
2010-03-30 09:50:09 +02:00
goto retry ;
}
2002-09-25 15:19:00 +00: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 11:18:56 +00:00
}
2006-01-25 21:25:25 +00:00
# endif /* HAVE_LDAP */
2001-12-08 11:18:56 +00:00