2010-07-23 22:47:36 +04:00
/*
* GSSAPI Security Extensions
2010-08-20 12:45:56 +04:00
* RPC Pipe client and server routines
2010-07-23 22:47:36 +04:00
* Copyright ( C ) Simo Sorce 2010.
2011-12-28 02:55:55 +04:00
* Copyright ( C ) Andrew Bartlett 2004 - 2011.
* Copyright ( C ) Stefan Metzmacher < metze @ samba . org > 2004 - 2005
2010-07-23 22:47:36 +04: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
* the Free Software Foundation ; either version 3 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 , see < http : //www.gnu.org/licenses/>.
*/
/* We support only GSSAPI/KRB5 here */
# include "includes.h"
2010-09-01 19:58:33 +04:00
# include "gse.h"
2012-01-04 04:39:38 +04:00
# include "libads/kerberos_proto.h"
2011-12-28 02:55:55 +04:00
# include "auth/common_auth.h"
# include "auth/gensec/gensec.h"
# include "auth/credentials/credentials.h"
# include "../librpc/gen_ndr/dcerpc.h"
2010-07-29 01:06:14 +04:00
2011-04-16 03:22:08 +04:00
# if defined(HAVE_KRB5) && defined(HAVE_GSS_WRAP_IOV)
2010-08-20 12:45:56 +04:00
# include "smb_krb5.h"
2010-09-01 19:58:33 +04:00
# include "gse_krb5.h"
2010-07-29 01:06:14 +04:00
2012-01-02 14:04:57 +04:00
# ifndef GSS_C_DCE_STYLE
# define GSS_C_DCE_STYLE 0x1000
# endif
2010-08-02 20:15:43 +04:00
# ifndef GSS_KRB5_INQ_SSPI_SESSION_KEY_OID
# define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11
# define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05"
# endif
2010-08-20 17:30:49 +04:00
gss_OID_desc gse_sesskey_inq_oid = {
GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH ,
( void * ) GSS_KRB5_INQ_SSPI_SESSION_KEY_OID
} ;
2010-08-02 20:15:43 +04:00
# ifndef GSS_KRB5_SESSION_KEY_ENCTYPE_OID
# define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10
# define GSS_KRB5_SESSION_KEY_ENCTYPE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04"
# endif
2010-08-20 17:30:49 +04:00
gss_OID_desc gse_sesskeytype_oid = {
GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH ,
( void * ) GSS_KRB5_SESSION_KEY_ENCTYPE_OID
} ;
# define GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID_LENGTH 12
/* EXTRACTION OID AUTHZ ID */
# define GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a" "\x01"
gss_OID_desc gse_authz_data_oid = {
GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID_LENGTH ,
( void * ) GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID
} ;
2010-08-02 20:15:43 +04:00
2010-07-23 22:47:36 +04:00
static char * gse_errstr ( TALLOC_CTX * mem_ctx , OM_uint32 maj , OM_uint32 min ) ;
struct gse_context {
krb5_context k5ctx ;
krb5_ccache ccache ;
2010-08-20 12:45:56 +04:00
krb5_keytab keytab ;
2010-07-23 22:47:36 +04:00
2012-01-11 04:18:16 +04:00
gss_ctx_id_t gssapi_context ;
2010-07-23 22:47:36 +04:00
gss_OID_desc gss_mech ;
2012-01-11 04:29:01 +04:00
OM_uint32 gss_want_flags ;
2010-08-20 12:45:56 +04:00
gss_cred_id_t creds ;
2010-07-23 22:47:36 +04:00
gss_name_t server_name ;
2010-08-20 12:45:56 +04:00
gss_OID ret_mech ;
2012-01-11 04:29:01 +04:00
OM_uint32 gss_got_flags ;
2012-01-11 04:17:26 +04:00
gss_cred_id_t delegated_cred_handle ;
2010-08-20 12:45:56 +04:00
gss_name_t client_name ;
2010-07-23 22:47:36 +04:00
} ;
2011-04-16 09:39:00 +04:00
# ifndef HAVE_GSS_OID_EQUAL
static bool gss_oid_equal ( const gss_OID o1 , const gss_OID o2 )
{
if ( o1 = = o2 ) {
return true ;
}
if ( ( o1 = = NULL & & o2 ! = NULL ) | | ( o1 ! = NULL & & o2 = = NULL ) ) {
return false ;
}
if ( o1 - > length ! = o2 - > length ) {
return false ;
}
return memcmp ( o1 - > elements , o2 - > elements , o1 - > length ) = = false ;
}
# endif
2010-07-23 22:47:36 +04:00
/* free non talloc dependent contexts */
static int gse_context_destructor ( void * ptr )
{
struct gse_context * gse_ctx ;
OM_uint32 gss_min , gss_maj ;
gse_ctx = talloc_get_type_abort ( ptr , struct gse_context ) ;
if ( gse_ctx - > k5ctx ) {
if ( gse_ctx - > ccache ) {
krb5_cc_close ( gse_ctx - > k5ctx , gse_ctx - > ccache ) ;
gse_ctx - > ccache = NULL ;
}
2010-08-20 12:45:56 +04:00
if ( gse_ctx - > keytab ) {
krb5_kt_close ( gse_ctx - > k5ctx , gse_ctx - > keytab ) ;
gse_ctx - > keytab = NULL ;
}
2010-07-23 22:47:36 +04:00
krb5_free_context ( gse_ctx - > k5ctx ) ;
gse_ctx - > k5ctx = NULL ;
}
2012-01-11 04:18:16 +04:00
if ( gse_ctx - > gssapi_context ! = GSS_C_NO_CONTEXT ) {
2010-07-23 22:47:36 +04:00
gss_maj = gss_delete_sec_context ( & gss_min ,
2012-01-11 04:18:16 +04:00
& gse_ctx - > gssapi_context ,
2010-07-23 22:47:36 +04:00
GSS_C_NO_BUFFER ) ;
}
if ( gse_ctx - > server_name ) {
gss_maj = gss_release_name ( & gss_min ,
& gse_ctx - > server_name ) ;
}
2010-08-20 12:45:56 +04:00
if ( gse_ctx - > client_name ) {
gss_maj = gss_release_name ( & gss_min ,
& gse_ctx - > client_name ) ;
}
if ( gse_ctx - > creds ) {
gss_maj = gss_release_cred ( & gss_min ,
& gse_ctx - > creds ) ;
}
2012-01-11 04:17:26 +04:00
if ( gse_ctx - > delegated_cred_handle ) {
2010-08-20 12:45:56 +04:00
gss_maj = gss_release_cred ( & gss_min ,
2012-01-11 04:17:26 +04:00
& gse_ctx - > delegated_cred_handle ) ;
2010-08-20 12:45:56 +04:00
}
2011-04-16 09:39:00 +04:00
/* MIT and Heimdal differ as to if you can call
* gss_release_oid ( ) on this OID , generated by
* gss_ { accept , init } _sec_context ( ) . However , as long as the
* oid is gss_mech_krb5 ( which it always is at the moment ) ,
* then this is a moot point , as both declare this particular
* OID static , and so no memory is lost . This assert is in
* place to ensure that the programmer who wishes to extend
* this code to EAP or other GSS mechanisms determines an
* implementation - dependent way of releasing any dynamically
* allocated OID */
SMB_ASSERT ( gss_oid_equal ( & gse_ctx - > gss_mech , GSS_C_NO_OID ) | | gss_oid_equal ( & gse_ctx - > gss_mech , gss_mech_krb5 ) ) ;
2010-07-23 22:47:36 +04:00
return 0 ;
}
static NTSTATUS gse_context_init ( TALLOC_CTX * mem_ctx ,
2010-09-02 01:27:56 +04:00
bool do_sign , bool do_seal ,
2010-07-23 22:47:36 +04:00
const char * ccache_name ,
uint32_t add_gss_c_flags ,
struct gse_context * * _gse_ctx )
{
struct gse_context * gse_ctx ;
krb5_error_code k5ret ;
NTSTATUS status ;
gse_ctx = talloc_zero ( mem_ctx , struct gse_context ) ;
if ( ! gse_ctx ) {
return NT_STATUS_NO_MEMORY ;
}
talloc_set_destructor ( ( TALLOC_CTX * ) gse_ctx , gse_context_destructor ) ;
memcpy ( & gse_ctx - > gss_mech , gss_mech_krb5 , sizeof ( gss_OID_desc ) ) ;
2012-01-11 04:29:01 +04:00
gse_ctx - > gss_want_flags = GSS_C_MUTUAL_FLAG |
2010-07-23 22:47:36 +04:00
GSS_C_DELEG_FLAG |
GSS_C_DELEG_POLICY_FLAG |
GSS_C_REPLAY_FLAG |
GSS_C_SEQUENCE_FLAG ;
2010-09-02 01:27:56 +04:00
if ( do_sign ) {
2012-01-11 04:29:01 +04:00
gse_ctx - > gss_want_flags | = GSS_C_INTEG_FLAG ;
2010-09-02 01:27:56 +04:00
}
if ( do_seal ) {
2012-01-11 04:29:01 +04:00
gse_ctx - > gss_want_flags | = GSS_C_CONF_FLAG ;
2010-07-23 22:47:36 +04:00
}
2012-01-11 04:29:01 +04:00
gse_ctx - > gss_want_flags | = add_gss_c_flags ;
2010-07-23 22:47:36 +04:00
/* Initialize Kerberos Context */
initialize_krb5_error_table ( ) ;
k5ret = krb5_init_context ( & gse_ctx - > k5ctx ) ;
if ( k5ret ) {
DEBUG ( 0 , ( " Failed to initialize kerberos context! (%s) \n " ,
error_message ( k5ret ) ) ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto err_out ;
}
if ( ! ccache_name ) {
ccache_name = krb5_cc_default_name ( gse_ctx - > k5ctx ) ;
}
k5ret = krb5_cc_resolve ( gse_ctx - > k5ctx , ccache_name ,
& gse_ctx - > ccache ) ;
if ( k5ret ) {
DEBUG ( 1 , ( " Failed to resolve credential cache! (%s) \n " ,
error_message ( k5ret ) ) ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto err_out ;
}
/* TODO: Should we enforce a enc_types list ?
ret = krb5_set_default_tgs_ktypes ( gse_ctx - > k5ctx , enc_types ) ;
*/
* _gse_ctx = gse_ctx ;
return NT_STATUS_OK ;
err_out :
TALLOC_FREE ( gse_ctx ) ;
return status ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_init_client ( TALLOC_CTX * mem_ctx ,
bool do_sign , bool do_seal ,
const char * ccache_name ,
const char * server ,
const char * service ,
const char * username ,
const char * password ,
uint32_t add_gss_c_flags ,
struct gse_context * * _gse_ctx )
2010-07-23 22:47:36 +04:00
{
struct gse_context * gse_ctx ;
OM_uint32 gss_maj , gss_min ;
gss_buffer_desc name_buffer = { 0 , NULL } ;
gss_OID_set_desc mech_set ;
NTSTATUS status ;
if ( ! server | | ! service ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2010-09-02 01:27:56 +04:00
status = gse_context_init ( mem_ctx , do_sign , do_seal ,
2010-07-23 22:47:36 +04:00
ccache_name , add_gss_c_flags ,
& gse_ctx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-28 23:53:56 +04:00
return NT_STATUS_NO_MEMORY ;
2010-07-23 22:47:36 +04:00
}
2012-01-04 04:39:38 +04:00
/* Guess the realm based on the supplied service, and avoid the GSS libs
doing DNS lookups which may fail .
TODO : Loop with the KDC on some more combinations ( local
realm in particular ) , possibly falling back to
GSS_C_NT_HOSTBASED_SERVICE
*/
name_buffer . value = kerberos_get_principal_from_service_hostname ( gse_ctx ,
service , server ) ;
2010-07-23 22:47:36 +04:00
if ( ! name_buffer . value ) {
status = NT_STATUS_NO_MEMORY ;
goto err_out ;
}
name_buffer . length = strlen ( ( char * ) name_buffer . value ) ;
gss_maj = gss_import_name ( & gss_min , & name_buffer ,
2012-01-04 04:39:38 +04:00
GSS_C_NT_USER_NAME ,
2010-07-23 22:47:36 +04:00
& gse_ctx - > server_name ) ;
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_import_name failed for %s, with [%s] \n " ,
( char * ) name_buffer . value ,
2010-07-28 23:53:56 +04:00
gse_errstr ( gse_ctx , gss_maj , gss_min ) ) ) ;
2010-07-23 22:47:36 +04:00
status = NT_STATUS_INTERNAL_ERROR ;
goto err_out ;
}
/* TODO: get krb5 ticket using username/password, if no valid
* one already available in ccache */
mech_set . count = 1 ;
mech_set . elements = & gse_ctx - > gss_mech ;
gss_maj = gss_acquire_cred ( & gss_min ,
2010-07-24 18:35:25 +04:00
GSS_C_NO_NAME ,
2010-07-23 22:47:36 +04:00
GSS_C_INDEFINITE ,
& mech_set ,
GSS_C_INITIATE ,
2010-08-20 12:45:56 +04:00
& gse_ctx - > creds ,
2010-07-23 22:47:36 +04:00
NULL , NULL ) ;
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_acquire_creds failed for %s, with [%s] \n " ,
( char * ) name_buffer . value ,
2010-07-28 23:53:56 +04:00
gse_errstr ( gse_ctx , gss_maj , gss_min ) ) ) ;
2010-07-23 22:47:36 +04:00
status = NT_STATUS_INTERNAL_ERROR ;
goto err_out ;
}
2010-07-28 23:53:56 +04:00
* _gse_ctx = gse_ctx ;
2010-07-23 22:47:36 +04:00
TALLOC_FREE ( name_buffer . value ) ;
return NT_STATUS_OK ;
err_out :
2010-07-28 23:53:56 +04:00
TALLOC_FREE ( name_buffer . value ) ;
TALLOC_FREE ( gse_ctx ) ;
2010-07-23 22:47:36 +04:00
return status ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_get_client_auth_token ( TALLOC_CTX * mem_ctx ,
struct gse_context * gse_ctx ,
2012-01-02 13:38:31 +04:00
const DATA_BLOB * token_in ,
2012-01-02 13:30:41 +04:00
DATA_BLOB * token_out )
2010-07-23 22:47:36 +04:00
{
OM_uint32 gss_maj , gss_min ;
gss_buffer_desc in_data ;
gss_buffer_desc out_data ;
DATA_BLOB blob = data_blob_null ;
NTSTATUS status ;
in_data . value = token_in - > data ;
in_data . length = token_in - > length ;
gss_maj = gss_init_sec_context ( & gss_min ,
2010-08-20 12:45:56 +04:00
gse_ctx - > creds ,
2012-01-11 04:18:16 +04:00
& gse_ctx - > gssapi_context ,
2010-07-23 22:47:36 +04:00
gse_ctx - > server_name ,
& gse_ctx - > gss_mech ,
2012-01-11 04:29:01 +04:00
gse_ctx - > gss_want_flags ,
2010-07-23 22:47:36 +04:00
0 , GSS_C_NO_CHANNEL_BINDINGS ,
& in_data , NULL , & out_data ,
2012-01-11 04:29:01 +04:00
& gse_ctx - > gss_got_flags , NULL ) ;
2010-07-23 22:47:36 +04:00
switch ( gss_maj ) {
case GSS_S_COMPLETE :
/* we are done with it */
status = NT_STATUS_OK ;
break ;
case GSS_S_CONTINUE_NEEDED :
/* we will need a third leg */
2012-01-11 04:36:58 +04:00
status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
2010-07-23 22:47:36 +04:00
break ;
default :
DEBUG ( 0 , ( " gss_init_sec_context failed with [%s] \n " ,
gse_errstr ( talloc_tos ( ) , gss_maj , gss_min ) ) ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
}
blob = data_blob_talloc ( mem_ctx , out_data . value , out_data . length ) ;
if ( ! blob . data ) {
status = NT_STATUS_NO_MEMORY ;
}
gss_maj = gss_release_buffer ( & gss_min , & out_data ) ;
done :
* token_out = blob ;
return status ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_init_server ( TALLOC_CTX * mem_ctx ,
bool do_sign , bool do_seal ,
uint32_t add_gss_c_flags ,
struct gse_context * * _gse_ctx )
2010-08-20 12:45:56 +04:00
{
struct gse_context * gse_ctx ;
OM_uint32 gss_maj , gss_min ;
krb5_error_code ret ;
NTSTATUS status ;
2010-09-02 01:27:56 +04:00
status = gse_context_init ( mem_ctx , do_sign , do_seal ,
2010-08-20 12:45:56 +04:00
NULL , add_gss_c_flags , & gse_ctx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return NT_STATUS_NO_MEMORY ;
}
2011-04-16 02:50:53 +04:00
ret = gse_krb5_get_server_keytab ( gse_ctx - > k5ctx ,
& gse_ctx - > keytab ) ;
if ( ret ) {
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
2010-08-20 12:45:56 +04:00
}
2011-04-16 02:50:53 +04:00
# ifdef HAVE_GSS_KRB5_IMPORT_CRED
2011-07-20 06:06:30 +04:00
/* This creates a GSSAPI cred_id_t with the keytab set */
2011-04-16 02:50:53 +04:00
gss_maj = gss_krb5_import_cred ( & gss_min , NULL , NULL , gse_ctx - > keytab ,
2011-07-20 06:06:30 +04:00
& gse_ctx - > creds ) ;
if ( gss_maj ! = 0
& & gss_maj ! = ( GSS_S_CALL_BAD_STRUCTURE | GSS_S_BAD_NAME ) ) {
2011-04-16 02:50:53 +04:00
DEBUG ( 0 , ( " gss_krb5_import_cred failed with [%s] \n " ,
gse_errstr ( gse_ctx , gss_maj , gss_min ) ) ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
2011-07-20 06:06:30 +04:00
/* This is the error the MIT krb5 1.9 gives when it
* implements the function , but we do not specify the
* principal . However , when we specify the principal
* as host $ @ REALM the GSS acceptor fails with ' wrong
* principal in request ' . Work around the issue by
* falling back to the alternate approach below . */
} else if ( gss_maj = = ( GSS_S_CALL_BAD_STRUCTURE | GSS_S_BAD_NAME ) )
# endif
2010-08-20 12:45:56 +04:00
/* FIXME!!!
* This call sets the default keytab for the whole server , not
* just for this context . Need to find a way that does not alter
* the state of the whole server . . . */
2011-05-05 21:41:59 +04:00
{
const char * ktname ;
gss_OID_set_desc mech_set ;
2011-04-16 02:50:53 +04:00
2011-05-05 21:41:59 +04:00
ret = smb_krb5_keytab_name ( gse_ctx , gse_ctx - > k5ctx ,
2011-04-16 02:50:53 +04:00
gse_ctx - > keytab , & ktname ) ;
2011-05-05 21:41:59 +04:00
if ( ret ) {
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
}
2011-04-16 02:50:53 +04:00
2011-05-05 21:41:59 +04:00
ret = gsskrb5_register_acceptor_identity ( ktname ) ;
if ( ret ) {
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
}
2010-08-20 12:45:56 +04:00
2011-05-05 21:41:59 +04:00
mech_set . count = 1 ;
mech_set . elements = & gse_ctx - > gss_mech ;
gss_maj = gss_acquire_cred ( & gss_min ,
2010-08-20 12:45:56 +04:00
GSS_C_NO_NAME ,
GSS_C_INDEFINITE ,
& mech_set ,
GSS_C_ACCEPT ,
& gse_ctx - > creds ,
NULL , NULL ) ;
2011-04-16 02:50:53 +04:00
2011-05-05 21:41:59 +04:00
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_acquire_creds failed with [%s] \n " ,
gse_errstr ( gse_ctx , gss_maj , gss_min ) ) ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
}
2010-08-20 12:45:56 +04:00
}
2011-07-20 06:06:30 +04:00
2010-08-20 12:45:56 +04:00
status = NT_STATUS_OK ;
done :
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( gse_ctx ) ;
}
* _gse_ctx = gse_ctx ;
return status ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_get_server_auth_token ( TALLOC_CTX * mem_ctx ,
struct gse_context * gse_ctx ,
2012-01-02 13:38:31 +04:00
const DATA_BLOB * token_in ,
2012-01-02 13:30:41 +04:00
DATA_BLOB * token_out )
2010-08-20 12:45:56 +04:00
{
OM_uint32 gss_maj , gss_min ;
gss_buffer_desc in_data ;
gss_buffer_desc out_data ;
DATA_BLOB blob = data_blob_null ;
NTSTATUS status ;
in_data . value = token_in - > data ;
in_data . length = token_in - > length ;
gss_maj = gss_accept_sec_context ( & gss_min ,
2012-01-11 04:18:16 +04:00
& gse_ctx - > gssapi_context ,
2010-08-20 12:45:56 +04:00
gse_ctx - > creds ,
& in_data ,
GSS_C_NO_CHANNEL_BINDINGS ,
& gse_ctx - > client_name ,
& gse_ctx - > ret_mech ,
& out_data ,
2012-01-11 04:29:01 +04:00
& gse_ctx - > gss_got_flags , NULL ,
2012-01-11 04:17:26 +04:00
& gse_ctx - > delegated_cred_handle ) ;
2010-08-20 12:45:56 +04:00
switch ( gss_maj ) {
case GSS_S_COMPLETE :
/* we are done with it */
status = NT_STATUS_OK ;
break ;
case GSS_S_CONTINUE_NEEDED :
/* we will need a third leg */
2012-01-11 04:36:58 +04:00
status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
2010-08-20 12:45:56 +04:00
break ;
default :
DEBUG ( 0 , ( " gss_init_sec_context failed with [%s] \n " ,
gse_errstr ( talloc_tos ( ) , gss_maj , gss_min ) ) ) ;
2012-01-11 04:18:16 +04:00
if ( gse_ctx - > gssapi_context ) {
2010-08-20 12:45:56 +04:00
gss_delete_sec_context ( & gss_min ,
2012-01-11 04:18:16 +04:00
& gse_ctx - > gssapi_context ,
2010-08-20 12:45:56 +04:00
GSS_C_NO_BUFFER ) ;
}
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
}
/* we may be told to return nothing */
if ( out_data . length ) {
blob = data_blob_talloc ( mem_ctx , out_data . value , out_data . length ) ;
if ( ! blob . data ) {
status = NT_STATUS_NO_MEMORY ;
}
gss_maj = gss_release_buffer ( & gss_min , & out_data ) ;
}
done :
* token_out = blob ;
return status ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_verify_server_auth_flags ( struct gse_context * gse_ctx )
2010-08-20 12:45:56 +04:00
{
2010-08-20 17:31:07 +04:00
if ( memcmp ( gse_ctx - > ret_mech ,
gss_mech_krb5 , sizeof ( gss_OID_desc ) ) ! = 0 ) {
return NT_STATUS_ACCESS_DENIED ;
}
/* GSS_C_MUTUAL_FLAG */
/* GSS_C_DELEG_FLAG */
/* GSS_C_DELEG_POLICY_FLAG */
/* GSS_C_REPLAY_FLAG */
/* GSS_C_SEQUENCE_FLAG */
/* GSS_C_INTEG_FLAG */
2012-01-11 04:29:01 +04:00
if ( gse_ctx - > gss_want_flags & GSS_C_INTEG_FLAG ) {
if ( ! ( gse_ctx - > gss_got_flags & GSS_C_INTEG_FLAG ) ) {
2010-08-20 17:31:07 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
}
/* GSS_C_CONF_FLAG */
2012-01-11 04:29:01 +04:00
if ( gse_ctx - > gss_want_flags & GSS_C_CONF_FLAG ) {
if ( ! ( gse_ctx - > gss_got_flags & GSS_C_CONF_FLAG ) ) {
2010-08-20 17:31:07 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
}
2010-08-20 12:45:56 +04:00
2012-01-14 14:27:21 +04:00
/* GSS_C_DCE_STYLE */
if ( gse_ctx - > gss_want_flags & GSS_C_DCE_STYLE ) {
if ( ! ( gse_ctx - > gss_got_flags & GSS_C_DCE_STYLE ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
/* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
if ( ! ( gse_ctx - > gss_got_flags & GSS_C_MUTUAL_FLAG ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
}
2010-08-20 12:45:56 +04:00
return NT_STATUS_OK ;
}
2010-07-23 22:47:36 +04:00
static char * gse_errstr ( TALLOC_CTX * mem_ctx , OM_uint32 maj , OM_uint32 min )
{
OM_uint32 gss_min , gss_maj ;
gss_buffer_desc msg_min ;
gss_buffer_desc msg_maj ;
OM_uint32 msg_ctx = 0 ;
char * errstr = NULL ;
ZERO_STRUCT ( msg_min ) ;
ZERO_STRUCT ( msg_maj ) ;
gss_maj = gss_display_status ( & gss_min , maj , GSS_C_GSS_CODE ,
GSS_C_NO_OID , & msg_ctx , & msg_maj ) ;
if ( gss_maj ) {
goto done ;
}
2011-07-20 06:04:45 +04:00
errstr = talloc_strndup ( mem_ctx ,
( char * ) msg_maj . value ,
msg_maj . length ) ;
if ( ! errstr ) {
goto done ;
}
2010-07-23 22:47:36 +04:00
gss_maj = gss_display_status ( & gss_min , min , GSS_C_MECH_CODE ,
2010-08-01 17:34:52 +04:00
( gss_OID ) discard_const ( gss_mech_krb5 ) ,
2010-07-23 22:47:36 +04:00
& msg_ctx , & msg_min ) ;
if ( gss_maj ) {
goto done ;
}
errstr = talloc_strdup_append_buffer ( errstr , " : " ) ;
if ( ! errstr ) {
goto done ;
}
errstr = talloc_strndup_append_buffer ( errstr ,
( char * ) msg_min . value ,
msg_min . length ) ;
if ( ! errstr ) {
goto done ;
}
done :
if ( msg_min . value ) {
gss_maj = gss_release_buffer ( & gss_min , & msg_min ) ;
}
if ( msg_maj . value ) {
gss_maj = gss_release_buffer ( & gss_min , & msg_maj ) ;
}
return errstr ;
}
2012-01-02 13:30:41 +04:00
static DATA_BLOB gse_get_session_key ( TALLOC_CTX * mem_ctx ,
struct gse_context * gse_ctx )
2010-07-23 22:47:36 +04:00
{
2010-08-02 20:15:43 +04:00
OM_uint32 gss_min , gss_maj ;
gss_buffer_set_t set = GSS_C_NO_BUFFER_SET ;
DATA_BLOB ret ;
gss_maj = gss_inquire_sec_context_by_oid (
2012-01-11 04:18:16 +04:00
& gss_min , gse_ctx - > gssapi_context ,
2010-08-02 20:15:43 +04:00
& gse_sesskey_inq_oid , & set ) ;
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_inquire_sec_context_by_oid failed [%s] \n " ,
gse_errstr ( talloc_tos ( ) , gss_maj , gss_min ) ) ) ;
return data_blob_null ;
}
if ( ( set = = GSS_C_NO_BUFFER_SET ) | |
( set - > count ! = 2 ) | |
( memcmp ( set - > elements [ 1 ] . value ,
gse_sesskeytype_oid . elements ,
gse_sesskeytype_oid . length ) ! = 0 ) ) {
2012-01-02 15:17:06 +04:00
# ifdef HAVE_GSSKRB5_GET_SUBKEY
krb5_keyblock * subkey ;
gss_maj = gsskrb5_get_subkey ( & gss_min ,
2012-01-11 04:18:16 +04:00
gse_ctx - > gssapi_context ,
2012-01-02 15:17:06 +04:00
& subkey ) ;
if ( gss_maj ! = 0 ) {
DEBUG ( 1 , ( " NO session key for this mech \n " ) ) ;
return data_blob_null ;
}
ret = data_blob_talloc ( mem_ctx ,
KRB5_KEY_DATA ( subkey ) , KRB5_KEY_LENGTH ( subkey ) ) ;
krb5_free_keyblock ( NULL /* should be krb5_context */ , subkey ) ;
return ret ;
# else
2010-08-02 20:15:43 +04:00
DEBUG ( 0 , ( " gss_inquire_sec_context_by_oid returned unknown "
" OID for data in results: \n " ) ) ;
2010-08-17 15:06:36 +04:00
dump_data ( 1 , ( uint8_t * ) set - > elements [ 1 ] . value ,
2010-08-02 20:15:43 +04:00
set - > elements [ 1 ] . length ) ;
return data_blob_null ;
2012-01-02 15:17:06 +04:00
# endif
2010-08-02 20:15:43 +04:00
}
ret = data_blob_talloc ( mem_ctx , set - > elements [ 0 ] . value ,
set - > elements [ 0 ] . length ) ;
gss_maj = gss_release_buffer_set ( & gss_min , & set ) ;
return ret ;
2010-07-23 22:47:36 +04:00
}
2012-01-02 13:30:41 +04:00
static size_t gse_get_signature_length ( struct gse_context * gse_ctx ,
2012-01-03 17:42:35 +04:00
bool seal , size_t payload_size )
2010-07-24 21:02:57 +04:00
{
OM_uint32 gss_min , gss_maj ;
gss_iov_buffer_desc iov [ 2 ] ;
int sealed ;
2012-01-06 19:58:51 +04:00
/*
* gss_wrap_iov_length ( ) only needs the type and length
*/
2010-07-24 21:02:57 +04:00
iov [ 0 ] . type = GSS_IOV_BUFFER_TYPE_HEADER ;
iov [ 0 ] . buffer . value = NULL ;
iov [ 0 ] . buffer . length = 0 ;
iov [ 1 ] . type = GSS_IOV_BUFFER_TYPE_DATA ;
2012-01-06 19:58:51 +04:00
iov [ 1 ] . buffer . value = NULL ;
2010-07-24 21:02:57 +04:00
iov [ 1 ] . buffer . length = payload_size ;
2012-01-11 04:18:16 +04:00
gss_maj = gss_wrap_iov_length ( & gss_min , gse_ctx - > gssapi_context ,
2010-07-24 21:02:57 +04:00
seal , GSS_C_QOP_DEFAULT ,
& sealed , iov , 2 ) ;
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_wrap_iov_length failed with [%s] \n " ,
gse_errstr ( talloc_tos ( ) , gss_maj , gss_min ) ) ) ;
return 0 ;
}
return iov [ 0 ] . buffer . length ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_seal ( TALLOC_CTX * mem_ctx , struct gse_context * gse_ctx ,
DATA_BLOB * data , DATA_BLOB * signature )
2010-07-24 21:02:57 +04:00
{
OM_uint32 gss_min , gss_maj ;
gss_iov_buffer_desc iov [ 2 ] ;
int req_seal = 1 ; /* setting to 1 means we request sign+seal */
2012-01-05 17:59:20 +04:00
int sealed = 1 ;
2010-07-24 21:02:57 +04:00
NTSTATUS status ;
/* allocate the memory ourselves so we do not need to talloc_memdup */
2012-01-03 17:42:35 +04:00
signature - > length = gse_get_signature_length ( gse_ctx , true , data - > length ) ;
2010-07-24 21:02:57 +04:00
if ( ! signature - > length ) {
return NT_STATUS_INTERNAL_ERROR ;
}
2010-08-01 17:34:52 +04:00
signature - > data = ( uint8_t * ) talloc_size ( mem_ctx , signature - > length ) ;
2010-07-24 21:02:57 +04:00
if ( ! signature - > data ) {
return NT_STATUS_NO_MEMORY ;
}
iov [ 0 ] . type = GSS_IOV_BUFFER_TYPE_HEADER ;
iov [ 0 ] . buffer . value = signature - > data ;
iov [ 0 ] . buffer . length = signature - > length ;
/* data is encrypted in place, which is ok */
iov [ 1 ] . type = GSS_IOV_BUFFER_TYPE_DATA ;
iov [ 1 ] . buffer . value = data - > data ;
iov [ 1 ] . buffer . length = data - > length ;
2012-01-11 04:18:16 +04:00
gss_maj = gss_wrap_iov ( & gss_min , gse_ctx - > gssapi_context ,
2010-07-24 21:02:57 +04:00
req_seal , GSS_C_QOP_DEFAULT ,
& sealed , iov , 2 ) ;
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_wrap_iov failed with [%s] \n " ,
gse_errstr ( talloc_tos ( ) , gss_maj , gss_min ) ) ) ;
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
if ( ! sealed ) {
DEBUG ( 0 , ( " gss_wrap_iov says data was not sealed! \n " ) ) ;
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
status = NT_STATUS_OK ;
DEBUG ( 10 , ( " Sealed %d bytes, and got %d bytes header/signature. \n " ,
( int ) iov [ 1 ] . buffer . length , ( int ) iov [ 0 ] . buffer . length ) ) ;
done :
return status ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_unseal ( TALLOC_CTX * mem_ctx , struct gse_context * gse_ctx ,
2012-01-02 13:38:31 +04:00
DATA_BLOB * data , const DATA_BLOB * signature )
2010-07-24 21:02:57 +04:00
{
OM_uint32 gss_min , gss_maj ;
gss_iov_buffer_desc iov [ 2 ] ;
int sealed ;
NTSTATUS status ;
iov [ 0 ] . type = GSS_IOV_BUFFER_TYPE_HEADER ;
iov [ 0 ] . buffer . value = signature - > data ;
iov [ 0 ] . buffer . length = signature - > length ;
/* data is decrypted in place, which is ok */
iov [ 1 ] . type = GSS_IOV_BUFFER_TYPE_DATA ;
iov [ 1 ] . buffer . value = data - > data ;
iov [ 1 ] . buffer . length = data - > length ;
2012-01-11 04:18:16 +04:00
gss_maj = gss_unwrap_iov ( & gss_min , gse_ctx - > gssapi_context ,
2010-07-24 21:02:57 +04:00
& sealed , NULL , iov , 2 ) ;
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_unwrap_iov failed with [%s] \n " ,
gse_errstr ( talloc_tos ( ) , gss_maj , gss_min ) ) ) ;
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
if ( ! sealed ) {
DEBUG ( 0 , ( " gss_unwrap_iov says data is not sealed! \n " ) ) ;
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
status = NT_STATUS_OK ;
DEBUG ( 10 , ( " Unsealed %d bytes, with %d bytes header/signature. \n " ,
( int ) iov [ 1 ] . buffer . length , ( int ) iov [ 0 ] . buffer . length ) ) ;
done :
return status ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_sign ( TALLOC_CTX * mem_ctx , struct gse_context * gse_ctx ,
DATA_BLOB * data , DATA_BLOB * signature )
2010-07-24 21:02:57 +04:00
{
OM_uint32 gss_min , gss_maj ;
gss_buffer_desc in_data = { 0 , NULL } ;
gss_buffer_desc out_data = { 0 , NULL } ;
NTSTATUS status ;
in_data . value = data - > data ;
in_data . length = data - > length ;
2012-01-11 04:18:16 +04:00
gss_maj = gss_get_mic ( & gss_min , gse_ctx - > gssapi_context ,
2010-07-24 21:02:57 +04:00
GSS_C_QOP_DEFAULT ,
& in_data , & out_data ) ;
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_get_mic failed with [%s] \n " ,
gse_errstr ( talloc_tos ( ) , gss_maj , gss_min ) ) ) ;
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
* signature = data_blob_talloc ( mem_ctx ,
out_data . value , out_data . length ) ;
if ( ! signature - > data ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
status = NT_STATUS_OK ;
done :
if ( out_data . value ) {
gss_maj = gss_release_buffer ( & gss_min , & out_data ) ;
}
return status ;
}
2012-01-02 13:30:41 +04:00
static NTSTATUS gse_sigcheck ( TALLOC_CTX * mem_ctx , struct gse_context * gse_ctx ,
2012-01-02 13:38:31 +04:00
const DATA_BLOB * data , const DATA_BLOB * signature )
2010-07-24 21:02:57 +04:00
{
OM_uint32 gss_min , gss_maj ;
gss_buffer_desc in_data = { 0 , NULL } ;
gss_buffer_desc in_token = { 0 , NULL } ;
NTSTATUS status ;
in_data . value = data - > data ;
in_data . length = data - > length ;
in_token . value = signature - > data ;
in_token . length = signature - > length ;
2012-01-11 04:18:16 +04:00
gss_maj = gss_verify_mic ( & gss_min , gse_ctx - > gssapi_context ,
2010-07-24 21:02:57 +04:00
& in_data , & in_token , NULL ) ;
if ( gss_maj ) {
DEBUG ( 0 , ( " gss_verify_mic failed with [%s] \n " ,
gse_errstr ( talloc_tos ( ) , gss_maj , gss_min ) ) ) ;
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
status = NT_STATUS_OK ;
done :
return status ;
}
2011-12-28 02:55:55 +04:00
static NTSTATUS gensec_gse_client_start ( struct gensec_security * gensec_security )
{
struct gse_context * gse_ctx ;
struct cli_credentials * creds = gensec_get_credentials ( gensec_security ) ;
NTSTATUS nt_status ;
OM_uint32 want_flags = 0 ;
bool do_sign = false , do_seal = false ;
const char * hostname = gensec_get_target_hostname ( gensec_security ) ;
const char * service = gensec_get_target_service ( gensec_security ) ;
const char * username = cli_credentials_get_username ( creds ) ;
const char * password = cli_credentials_get_password ( creds ) ;
if ( ! hostname ) {
DEBUG ( 1 , ( " Could not determine hostname for target computer, cannot use kerberos \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( is_ipaddress ( hostname ) ) {
DEBUG ( 2 , ( " Cannot do GSE to an IP address \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( strcmp ( hostname , " localhost " ) = = 0 ) {
DEBUG ( 2 , ( " GSE to 'localhost' does not make sense \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( gensec_security - > want_features & GENSEC_FEATURE_SIGN ) {
do_sign = true ;
}
if ( gensec_security - > want_features & GENSEC_FEATURE_SEAL ) {
do_seal = true ;
}
if ( gensec_security - > want_features & GENSEC_FEATURE_DCE_STYLE ) {
want_flags | = GSS_C_DCE_STYLE ;
}
nt_status = gse_init_client ( gensec_security , do_sign , do_seal , NULL ,
hostname , service ,
username , password , want_flags ,
& gse_ctx ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return nt_status ;
}
gensec_security - > private_data = gse_ctx ;
return NT_STATUS_OK ;
}
static NTSTATUS gensec_gse_server_start ( struct gensec_security * gensec_security )
{
struct gse_context * gse_ctx ;
NTSTATUS nt_status ;
OM_uint32 want_flags = 0 ;
bool do_sign = false , do_seal = false ;
if ( gensec_security - > want_features & GENSEC_FEATURE_SIGN ) {
do_sign = true ;
}
if ( gensec_security - > want_features & GENSEC_FEATURE_SEAL ) {
do_seal = true ;
}
if ( gensec_security - > want_features & GENSEC_FEATURE_DCE_STYLE ) {
want_flags | = GSS_C_DCE_STYLE ;
}
nt_status = gse_init_server ( gensec_security , do_sign , do_seal , want_flags ,
& gse_ctx ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return nt_status ;
}
gensec_security - > private_data = gse_ctx ;
return NT_STATUS_OK ;
}
/**
* Check if the packet is one for this mechansim
*
* @ param gensec_security GENSEC state
* @ param in The request , as a DATA_BLOB
* @ return Error , INVALID_PARAMETER if it ' s not a packet for us
* or NT_STATUS_OK if the packet is ok .
*/
static NTSTATUS gensec_gse_magic ( struct gensec_security * gensec_security ,
const DATA_BLOB * in )
{
if ( gensec_gssapi_check_oid ( in , GENSEC_OID_KERBEROS5 ) ) {
return NT_STATUS_OK ;
} else {
return NT_STATUS_INVALID_PARAMETER ;
}
}
/**
* Next state function for the GSE GENSEC mechanism
*
* @ param gensec_gse_state GSE State
* @ param mem_ctx The TALLOC_CTX for * out to be allocated on
* @ param in The request , as a DATA_BLOB
* @ param out The reply , as an talloc ( ) ed DATA_BLOB , on * mem_ctx
* @ return Error , MORE_PROCESSING_REQUIRED if a reply is sent ,
* or NT_STATUS_OK if the user is authenticated .
*/
static NTSTATUS gensec_gse_update ( struct gensec_security * gensec_security ,
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
const DATA_BLOB in , DATA_BLOB * out )
{
NTSTATUS status ;
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
switch ( gensec_security - > gensec_role ) {
case GENSEC_CLIENT :
status = gse_get_client_auth_token ( mem_ctx , gse_ctx ,
& in , out ) ;
break ;
case GENSEC_SERVER :
status = gse_get_server_auth_token ( mem_ctx , gse_ctx ,
& in , out ) ;
break ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( gensec_security - > gensec_role = = GENSEC_SERVER ) {
return gse_verify_server_auth_flags ( gse_ctx ) ;
}
return NT_STATUS_OK ;
}
static NTSTATUS gensec_gse_wrap ( struct gensec_security * gensec_security ,
TALLOC_CTX * mem_ctx ,
const DATA_BLOB * in ,
DATA_BLOB * out )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
OM_uint32 maj_stat , min_stat ;
gss_buffer_desc input_token , output_token ;
int conf_state ;
input_token . length = in - > length ;
input_token . value = in - > data ;
maj_stat = gss_wrap ( & min_stat ,
2012-01-11 04:18:16 +04:00
gse_ctx - > gssapi_context ,
2011-12-28 02:55:55 +04:00
gensec_have_feature ( gensec_security , GENSEC_FEATURE_SEAL ) ,
GSS_C_QOP_DEFAULT ,
& input_token ,
& conf_state ,
& output_token ) ;
if ( GSS_ERROR ( maj_stat ) ) {
DEBUG ( 0 , ( " gensec_gse_wrap: GSS Wrap failed: %s \n " ,
gse_errstr ( talloc_tos ( ) , maj_stat , min_stat ) ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
* out = data_blob_talloc ( mem_ctx , output_token . value , output_token . length ) ;
gss_release_buffer ( & min_stat , & output_token ) ;
if ( gensec_have_feature ( gensec_security , GENSEC_FEATURE_SEAL )
& & ! conf_state ) {
return NT_STATUS_ACCESS_DENIED ;
}
return NT_STATUS_OK ;
}
static NTSTATUS gensec_gse_unwrap ( struct gensec_security * gensec_security ,
TALLOC_CTX * mem_ctx ,
const DATA_BLOB * in ,
DATA_BLOB * out )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
OM_uint32 maj_stat , min_stat ;
gss_buffer_desc input_token , output_token ;
int conf_state ;
gss_qop_t qop_state ;
input_token . length = in - > length ;
input_token . value = in - > data ;
maj_stat = gss_unwrap ( & min_stat ,
2012-01-11 04:18:16 +04:00
gse_ctx - > gssapi_context ,
2011-12-28 02:55:55 +04:00
& input_token ,
& output_token ,
& conf_state ,
& qop_state ) ;
if ( GSS_ERROR ( maj_stat ) ) {
DEBUG ( 0 , ( " gensec_gse_unwrap: GSS UnWrap failed: %s \n " ,
gse_errstr ( talloc_tos ( ) , maj_stat , min_stat ) ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
* out = data_blob_talloc ( mem_ctx , output_token . value , output_token . length ) ;
gss_release_buffer ( & min_stat , & output_token ) ;
if ( gensec_have_feature ( gensec_security , GENSEC_FEATURE_SEAL )
& & ! conf_state ) {
return NT_STATUS_ACCESS_DENIED ;
}
return NT_STATUS_OK ;
}
static NTSTATUS gensec_gse_seal_packet ( struct gensec_security * gensec_security ,
TALLOC_CTX * mem_ctx ,
uint8_t * data , size_t length ,
const uint8_t * whole_pdu , size_t pdu_length ,
DATA_BLOB * sig )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
DATA_BLOB payload = data_blob_const ( data , length ) ;
return gse_seal ( mem_ctx , gse_ctx , & payload , sig ) ;
}
static NTSTATUS gensec_gse_unseal_packet ( struct gensec_security * gensec_security ,
uint8_t * data , size_t length ,
const uint8_t * whole_pdu , size_t pdu_length ,
const DATA_BLOB * sig )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
DATA_BLOB payload = data_blob_const ( data , length ) ;
return gse_unseal ( talloc_tos ( ) /* unused */ , gse_ctx , & payload , sig ) ;
}
static NTSTATUS gensec_gse_sign_packet ( struct gensec_security * gensec_security ,
TALLOC_CTX * mem_ctx ,
const uint8_t * data , size_t length ,
const uint8_t * whole_pdu , size_t pdu_length ,
DATA_BLOB * sig )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
DATA_BLOB payload = data_blob_const ( data , length ) ;
return gse_sign ( mem_ctx , gse_ctx , & payload , sig ) ;
}
static NTSTATUS gensec_gse_check_packet ( struct gensec_security * gensec_security ,
const uint8_t * data , size_t length ,
const uint8_t * whole_pdu , size_t pdu_length ,
const DATA_BLOB * sig )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
DATA_BLOB payload = data_blob_const ( data , length ) ;
return gse_sigcheck ( NULL , gse_ctx , & payload , sig ) ;
}
/* Try to figure out what features we actually got on the connection */
static bool gensec_gse_have_feature ( struct gensec_security * gensec_security ,
uint32_t feature )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
if ( feature & GENSEC_FEATURE_SIGN ) {
2012-01-11 04:29:01 +04:00
return gse_ctx - > gss_got_flags & GSS_C_INTEG_FLAG ;
2011-12-28 02:55:55 +04:00
}
if ( feature & GENSEC_FEATURE_SEAL ) {
2012-01-11 04:29:01 +04:00
return gse_ctx - > gss_got_flags & GSS_C_CONF_FLAG ;
2011-12-28 02:55:55 +04:00
}
if ( feature & GENSEC_FEATURE_SESSION_KEY ) {
/* Only for GSE/Krb5 */
if ( gss_oid_equal ( gse_ctx - > ret_mech , gss_mech_krb5 ) ) {
return true ;
}
}
if ( feature & GENSEC_FEATURE_DCE_STYLE ) {
2012-01-11 04:29:01 +04:00
return gse_ctx - > gss_got_flags & GSS_C_DCE_STYLE ;
2011-12-28 02:55:55 +04:00
}
/* We can always do async (rather than strict request/reply) packets. */
if ( feature & GENSEC_FEATURE_ASYNC_REPLIES ) {
return true ;
}
return false ;
}
/*
* Extract the ' sesssion key ' needed by SMB signing and ncacn_np
* ( for encrypting some passwords ) .
*
* This breaks all the abstractions , but what do you expect . . .
*/
static NTSTATUS gensec_gse_session_key ( struct gensec_security * gensec_security ,
TALLOC_CTX * mem_ctx ,
DATA_BLOB * session_key_out )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
DATA_BLOB session_key = gse_get_session_key ( mem_ctx , gse_ctx ) ;
if ( session_key . data = = NULL ) {
return NT_STATUS_NO_USER_SESSION_KEY ;
}
* session_key_out = session_key ;
return NT_STATUS_OK ;
}
/* Get some basic (and authorization) information about the user on
* this session . This uses either the PAC ( if present ) or a local
* database lookup */
static NTSTATUS gensec_gse_session_info ( struct gensec_security * gensec_security ,
TALLOC_CTX * mem_ctx ,
struct auth_session_info * * _session_info )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
NTSTATUS nt_status ;
TALLOC_CTX * tmp_ctx ;
struct auth_session_info * session_info = NULL ;
OM_uint32 maj_stat , min_stat ;
DATA_BLOB pac_blob , * pac_blob_ptr = NULL ;
gss_buffer_desc name_token ;
char * principal_string ;
tmp_ctx = talloc_named ( mem_ctx , 0 , " gensec_gse_session_info context " ) ;
NT_STATUS_HAVE_NO_MEMORY ( tmp_ctx ) ;
maj_stat = gss_display_name ( & min_stat ,
gse_ctx - > client_name ,
& name_token ,
NULL ) ;
if ( GSS_ERROR ( maj_stat ) ) {
DEBUG ( 1 , ( " GSS display_name failed: %s \n " ,
gse_errstr ( talloc_tos ( ) , maj_stat , min_stat ) ) ) ;
talloc_free ( tmp_ctx ) ;
return NT_STATUS_FOOBAR ;
}
principal_string = talloc_strndup ( tmp_ctx ,
( const char * ) name_token . value ,
name_token . length ) ;
gss_release_buffer ( & min_stat , & name_token ) ;
if ( ! principal_string ) {
talloc_free ( tmp_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2012-01-11 04:18:16 +04:00
nt_status = gssapi_obtain_pac_blob ( tmp_ctx , gse_ctx - > gssapi_context ,
2011-12-28 02:55:55 +04:00
gse_ctx - > client_name ,
& pac_blob ) ;
/* IF we have the PAC - otherwise we need to get this
* data from elsewere
*/
if ( NT_STATUS_IS_OK ( nt_status ) ) {
pac_blob_ptr = & pac_blob ;
}
nt_status = gensec_generate_session_info_pac ( tmp_ctx ,
gensec_security ,
NULL ,
pac_blob_ptr , principal_string ,
gensec_get_remote_address ( gensec_security ) ,
& session_info ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
return nt_status ;
}
nt_status = gensec_gse_session_key ( gensec_security , session_info ,
& session_info - > session_key ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
return nt_status ;
}
* _session_info = talloc_move ( mem_ctx , & session_info ) ;
talloc_free ( tmp_ctx ) ;
return NT_STATUS_OK ;
}
static size_t gensec_gse_sig_size ( struct gensec_security * gensec_security ,
size_t data_size )
{
struct gse_context * gse_ctx =
talloc_get_type_abort ( gensec_security - > private_data ,
struct gse_context ) ;
return gse_get_signature_length ( gse_ctx ,
gensec_security - > want_features & GENSEC_FEATURE_SEAL ,
data_size ) ;
}
static const char * gensec_gse_krb5_oids [ ] = {
GENSEC_OID_KERBEROS5_OLD ,
GENSEC_OID_KERBEROS5 ,
NULL
} ;
2012-01-02 13:22:38 +04:00
const struct gensec_security_ops gensec_gse_krb5_security_ops = {
2011-12-28 02:55:55 +04:00
. name = " gse_krb5 " ,
. auth_type = DCERPC_AUTH_TYPE_KRB5 ,
. oid = gensec_gse_krb5_oids ,
. client_start = gensec_gse_client_start ,
. server_start = gensec_gse_server_start ,
. magic = gensec_gse_magic ,
. update = gensec_gse_update ,
. session_key = gensec_gse_session_key ,
. session_info = gensec_gse_session_info ,
. sig_size = gensec_gse_sig_size ,
. sign_packet = gensec_gse_sign_packet ,
. check_packet = gensec_gse_check_packet ,
. seal_packet = gensec_gse_seal_packet ,
. unseal_packet = gensec_gse_unseal_packet ,
. wrap = gensec_gse_wrap ,
. unwrap = gensec_gse_unwrap ,
. have_feature = gensec_gse_have_feature ,
. enabled = true ,
. kerberos = true ,
. priority = GENSEC_GSSAPI
} ;
2011-04-16 09:41:50 +04:00
# endif /* HAVE_KRB5 && HAVE_GSS_WRAP_IOV */