2004-06-19 12:15:41 +04:00
/*
Unix SMB / CIFS implementation .
RFC2478 Compliant SPNEGO implementation
2012-09-18 21:34:48 +04:00
2004-06-20 04:58:09 +04:00
Copyright ( C ) Jim McDonough < jmcd @ us . ibm . com > 2003
2005-09-02 03:23:22 +04:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2004 - 2005
2008-08-12 16:26:21 +04:00
Copyright ( C ) Stefan Metzmacher < metze @ samba . org > 2004 - 2008
2004-06-19 12:15:41 +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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-06-19 12:15:41 +04:00
( at your option ) any later version .
2012-09-18 21:34:48 +04:00
2004-06-19 12:15:41 +04: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 .
2012-09-18 21:34:48 +04:00
2004-06-19 12:15:41 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-06-19 12:15:41 +04:00
*/
# include "includes.h"
2017-05-11 10:04:02 +03:00
# include <tevent.h>
# include "lib/util/tevent_ntstatus.h"
2009-09-17 02:21:01 +04:00
# include "../libcli/auth/spnego.h"
2006-03-18 18:42:57 +03:00
# include "librpc/gen_ndr/ndr_dcerpc.h"
2006-11-07 03:48:36 +03:00
# include "auth/credentials/credentials.h"
# include "auth/gensec/gensec.h"
2013-08-05 09:12:01 +04:00
# include "auth/gensec/gensec_internal.h"
2010-05-13 01:59:41 +04:00
# include "param/param.h"
2011-12-24 03:27:45 +04:00
# include "lib/util/asn1.h"
2016-05-03 17:12:10 +03:00
# include "lib/util/base64.h"
2004-06-19 12:15:41 +04:00
2017-12-19 12:49:10 +03:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_AUTH
2012-01-12 18:42:08 +04:00
# undef strcasecmp
2017-04-20 22:24:43 +03:00
_PUBLIC_ NTSTATUS gensec_spnego_init ( TALLOC_CTX * ctx ) ;
2011-03-19 02:44:20 +03:00
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
enum spnego_state_position {
SPNEGO_SERVER_START ,
2004-07-06 07:02:33 +04:00
SPNEGO_CLIENT_START ,
2004-07-06 21:53:44 +04:00
SPNEGO_SERVER_TARG ,
SPNEGO_CLIENT_TARG ,
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
SPNEGO_FALLBACK ,
SPNEGO_DONE
} ;
2017-06-14 02:52:09 +03:00
struct spnego_state ;
struct spnego_neg_ops ;
struct spnego_neg_state ;
struct spnego_neg_state {
const struct spnego_neg_ops * ops ;
const struct gensec_security_ops_wrapper * all_sec ;
size_t all_idx ;
const char * const * mech_types ;
size_t mech_idx ;
} ;
struct spnego_neg_ops {
const char * name ;
/*
2023-03-14 10:50:34 +03:00
* The start hook does the initial processing on the incoming packet and
2017-06-14 02:52:09 +03:00
* may starts the first possible subcontext . It indicates that
* gensec_update ( ) is required on the subcontext by returning
* NT_STATUS_MORE_PROCESSING_REQUIRED and return something useful in
* ' in_next ' . Note that ' in_mem_ctx ' is just passed as a hint , the
* caller should treat ' in_next ' as const and don ' t attempt to free the
* content . NT_STATUS_OK indicates the finish hook should be invoked
2019-08-29 23:39:12 +03:00
* directly within the need of gensec_update ( ) on the subcontext .
2017-06-14 02:52:09 +03:00
* Every other error indicates an error that ' s returned to the caller .
*/
NTSTATUS ( * start_fn ) ( struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next ) ;
/*
* The step hook processes the result of a failed gensec_update ( ) and
* can decide to ignore a failure and continue the negotiation by
* setting up the next possible subcontext . It indicates that
* gensec_update ( ) is required on the subcontext by returning
* NT_STATUS_MORE_PROCESSING_REQUIRED and return something useful in
* ' in_next ' . Note that ' in_mem_ctx ' is just passed as a hint , the
* caller should treat ' in_next ' as const and don ' t attempt to free the
* content . NT_STATUS_OK indicates the finish hook should be invoked
2019-08-29 23:39:12 +03:00
* directly within the need of gensec_update ( ) on the subcontext .
2017-06-14 02:52:09 +03:00
* Every other error indicates an error that ' s returned to the caller .
*/
NTSTATUS ( * step_fn ) ( struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS last_status ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next ) ;
/*
* The finish hook processes the result of a successful gensec_update ( )
* ( NT_STATUS_OK or NT_STATUS_MORE_PROCESSING_REQUIRED ) . It forms the
* response pdu that will be returned from the toplevel gensec_update ( )
* together with NT_STATUS_OK or NT_STATUS_MORE_PROCESSING_REQUIRED . It
* may also alter the state machine to prepare receiving the next pdu
* from the peer .
*/
NTSTATUS ( * finish_fn ) ( struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS sub_status ,
const DATA_BLOB sub_out ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * out ) ;
} ;
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
struct spnego_state {
enum spnego_message_type expected_packet ;
2004-07-07 03:20:23 +04:00
enum spnego_state_position state_position ;
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
struct gensec_security * sub_sec_security ;
2016-12-30 11:03:08 +03:00
bool sub_sec_ready ;
2005-05-16 03:42:11 +04:00
const char * neg_oid ;
2008-08-12 16:26:21 +04:00
DATA_BLOB mech_types ;
2015-11-24 22:13:24 +03:00
size_t num_targs ;
2016-09-01 09:08:23 +03:00
bool downgraded ;
2015-11-24 22:13:24 +03:00
bool mic_requested ;
bool needs_mic_sign ;
bool needs_mic_check ;
2016-09-01 09:08:23 +03:00
bool may_skip_mic_check ;
2015-11-24 22:13:24 +03:00
bool done_mic_check ;
2011-12-24 03:27:45 +04:00
2016-04-25 15:45:55 +03:00
bool simulate_w2k ;
2019-09-04 16:31:21 +03:00
bool no_optimistic ;
2016-04-25 15:45:55 +03:00
2011-12-24 03:27:45 +04:00
/*
* The following is used to implement
* the update token fragmentation
*/
size_t in_needed ;
DATA_BLOB in_frag ;
size_t out_max_length ;
DATA_BLOB out_frag ;
NTSTATUS out_status ;
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
} ;
2017-06-14 02:52:09 +03:00
static struct spnego_neg_state * gensec_spnego_neg_state ( TALLOC_CTX * mem_ctx ,
const struct spnego_neg_ops * ops )
{
struct spnego_neg_state * n = NULL ;
n = talloc_zero ( mem_ctx , struct spnego_neg_state ) ;
if ( n = = NULL ) {
return NULL ;
}
n - > ops = ops ;
return n ;
}
2017-07-19 11:53:30 +03:00
static void gensec_spnego_reset_sub_sec ( struct spnego_state * spnego_state )
2017-05-10 15:44:48 +03:00
{
spnego_state - > sub_sec_ready = false ;
TALLOC_FREE ( spnego_state - > sub_sec_security ) ;
}
2004-12-24 12:54:23 +03:00
2007-12-03 19:41:50 +03:00
static NTSTATUS gensec_spnego_client_start ( struct gensec_security * gensec_security )
2004-06-19 12:15:41 +04:00
{
2004-06-20 04:58:09 +04:00
struct spnego_state * spnego_state ;
2004-09-26 06:16:25 +04:00
2011-12-24 03:27:45 +04:00
spnego_state = talloc_zero ( gensec_security , struct spnego_state ) ;
2004-06-20 04:58:09 +04:00
if ( ! spnego_state ) {
return NT_STATUS_NO_MEMORY ;
2004-06-19 12:15:41 +04:00
}
2004-06-20 04:58:09 +04:00
spnego_state - > expected_packet = SPNEGO_NEG_TOKEN_INIT ;
2004-07-06 07:02:33 +04:00
spnego_state - > state_position = SPNEGO_CLIENT_START ;
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
spnego_state - > sub_sec_security = NULL ;
2016-12-30 11:03:08 +03:00
spnego_state - > sub_sec_ready = false ;
2016-12-30 18:36:23 +03:00
spnego_state - > mech_types = data_blob_null ;
2011-12-24 03:27:45 +04:00
spnego_state - > out_max_length = gensec_max_update_size ( gensec_security ) ;
spnego_state - > out_status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
2004-06-19 12:15:41 +04:00
2016-04-25 15:45:55 +03:00
spnego_state - > simulate_w2k = gensec_setting_bool ( gensec_security - > settings ,
" spnego " , " simulate_w2k " , false ) ;
2019-09-04 16:31:21 +03:00
spnego_state - > no_optimistic = gensec_setting_bool ( gensec_security - > settings ,
" spnego " ,
" client_no_optimistic " ,
false ) ;
2016-04-25 15:45:55 +03:00
2004-06-20 04:58:09 +04:00
gensec_security - > private_data = spnego_state ;
return NT_STATUS_OK ;
2004-06-19 12:15:41 +04:00
}
2004-08-11 22:09:40 +04:00
static NTSTATUS gensec_spnego_server_start ( struct gensec_security * gensec_security )
{
struct spnego_state * spnego_state ;
2004-09-26 06:16:25 +04:00
2011-12-24 03:27:45 +04:00
spnego_state = talloc_zero ( gensec_security , struct spnego_state ) ;
2004-08-11 22:09:40 +04:00
if ( ! spnego_state ) {
return NT_STATUS_NO_MEMORY ;
}
spnego_state - > expected_packet = SPNEGO_NEG_TOKEN_INIT ;
spnego_state - > state_position = SPNEGO_SERVER_START ;
spnego_state - > sub_sec_security = NULL ;
2016-12-30 11:03:08 +03:00
spnego_state - > sub_sec_ready = false ;
2016-12-30 18:36:23 +03:00
spnego_state - > mech_types = data_blob_null ;
2011-12-24 03:27:45 +04:00
spnego_state - > out_max_length = gensec_max_update_size ( gensec_security ) ;
spnego_state - > out_status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
2004-08-11 22:09:40 +04:00
2016-04-25 15:45:55 +03:00
spnego_state - > simulate_w2k = gensec_setting_bool ( gensec_security - > settings ,
" spnego " , " simulate_w2k " , false ) ;
2004-08-11 22:09:40 +04:00
gensec_security - > private_data = spnego_state ;
return NT_STATUS_OK ;
}
2004-07-06 07:02:33 +04:00
/** Fallback to another GENSEC mechanism, based on magic strings
*
2004-07-12 13:11:13 +04:00
* This is the ' fallback ' case , where we don ' t get SPNEGO , and have to
2004-07-06 07:02:33 +04:00
* try all the other options ( and hope they all have a magic string
* they check )
*/
2004-07-12 13:11:13 +04:00
static NTSTATUS gensec_spnego_server_try_fallback ( struct gensec_security * gensec_security ,
2004-07-06 07:02:33 +04:00
struct spnego_state * spnego_state ,
2017-06-14 04:39:02 +03:00
TALLOC_CTX * mem_ctx ,
const DATA_BLOB in )
2004-07-06 07:02:33 +04:00
{
2005-05-16 03:42:11 +04:00
int i , j ;
2013-08-05 13:20:21 +04:00
const struct gensec_security_ops * * all_ops ;
2017-06-14 04:39:02 +03:00
all_ops = gensec_security_mechs ( gensec_security , mem_ctx ) ;
2013-08-05 13:20:21 +04:00
for ( i = 0 ; all_ops & & all_ops [ i ] ; i + + ) {
2007-10-07 02:16:19 +04:00
bool is_spnego ;
2004-07-06 07:02:33 +04:00
NTSTATUS nt_status ;
2008-11-02 21:28:17 +03:00
2017-07-19 11:47:37 +03:00
if ( gensec_security ! = NULL & &
! gensec_security_ops_enabled ( all_ops [ i ] , gensec_security ) )
{
continue ;
}
2008-11-02 21:28:17 +03:00
2004-07-06 07:02:33 +04:00
if ( ! all_ops [ i ] - > oid ) {
continue ;
}
2005-05-16 03:42:11 +04:00
2007-10-07 02:16:19 +04:00
is_spnego = false ;
2005-05-16 03:42:11 +04:00
for ( j = 0 ; all_ops [ i ] - > oid [ j ] ; j + + ) {
if ( strcasecmp ( GENSEC_OID_SPNEGO , all_ops [ i ] - > oid [ j ] ) = = 0 ) {
2007-10-07 02:16:19 +04:00
is_spnego = true ;
2005-05-16 03:42:11 +04:00
}
}
if ( is_spnego ) {
2004-11-15 23:31:12 +03:00
continue ;
}
2005-06-22 06:12:26 +04:00
if ( ! all_ops [ i ] - > magic ) {
continue ;
}
nt_status = all_ops [ i ] - > magic ( gensec_security , & in ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
continue ;
}
spnego_state - > state_position = SPNEGO_FALLBACK ;
2004-12-24 12:54:23 +03:00
nt_status = gensec_subcontext_start ( spnego_state ,
gensec_security ,
2004-07-06 07:02:33 +04:00
& spnego_state - > sub_sec_security ) ;
2005-06-22 06:12:26 +04:00
2004-07-06 07:02:33 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return nt_status ;
}
/* select the sub context */
2005-05-16 03:42:11 +04:00
nt_status = gensec_start_mech_by_ops ( spnego_state - > sub_sec_security ,
all_ops [ i ] ) ;
2004-07-06 07:02:33 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2005-06-22 06:12:26 +04:00
return nt_status ;
2004-07-06 07:02:33 +04:00
}
2017-06-14 04:39:02 +03:00
return NT_STATUS_OK ;
2004-07-06 07:02:33 +04:00
}
2004-07-12 13:11:13 +04:00
DEBUG ( 1 , ( " Failed to parse SPNEGO request \n " ) ) ;
2004-07-06 07:02:33 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
2017-06-14 02:52:09 +03:00
static NTSTATUS gensec_spnego_create_negTokenInit_start (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
2004-07-06 22:07:00 +04:00
{
2017-06-14 02:52:09 +03:00
n - > mech_idx = 0 ;
n - > mech_types = gensec_security_oids ( gensec_security , n ,
GENSEC_OID_SPNEGO ) ;
if ( n - > mech_types = = NULL ) {
2017-07-13 17:16:35 +03:00
DBG_WARNING ( " gensec_security_oids() failed \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
2004-07-06 22:07:00 +04:00
2017-06-14 02:52:09 +03:00
n - > all_idx = 0 ;
n - > all_sec = gensec_security_by_oid_list ( gensec_security ,
n , n - > mech_types ,
GENSEC_OID_SPNEGO ) ;
if ( n - > all_sec = = NULL ) {
2017-07-13 17:16:35 +03:00
DBG_WARNING ( " gensec_security_by_oid_list() failed \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
2017-06-14 02:52:09 +03:00
return n - > ops - > step_fn ( gensec_security , spnego_state , n ,
spnego_in , NT_STATUS_OK , in_mem_ctx , in_next ) ;
}
static NTSTATUS gensec_spnego_create_negTokenInit_step (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS last_status ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
{
if ( ! NT_STATUS_IS_OK ( last_status ) ) {
const struct gensec_security_ops_wrapper * cur_sec =
& n - > all_sec [ n - > all_idx ] ;
const struct gensec_security_ops_wrapper * next_sec = NULL ;
2017-07-13 16:49:32 +03:00
const char * next = NULL ;
const char * principal = NULL ;
int dbg_level = DBGLVL_WARNING ;
2017-06-14 02:52:09 +03:00
NTSTATUS status = last_status ;
2017-07-13 16:49:32 +03:00
2017-06-14 02:52:09 +03:00
if ( cur_sec [ 1 ] . op ! = NULL ) {
next_sec = & cur_sec [ 1 ] ;
2017-07-13 16:49:32 +03:00
}
2017-07-13 16:44:53 +03:00
2017-06-14 02:52:09 +03:00
if ( next_sec ! = NULL ) {
next = next_sec - > op - > name ;
2017-07-13 16:49:32 +03:00
dbg_level = DBGLVL_NOTICE ;
}
2017-07-13 16:44:53 +03:00
2017-07-13 16:49:32 +03:00
if ( gensec_security - > target . principal ! = NULL ) {
principal = gensec_security - > target . principal ;
} else if ( gensec_security - > target . service ! = NULL & &
gensec_security - > target . hostname ! = NULL )
{
principal = talloc_asprintf ( spnego_state - > sub_sec_security ,
" %s/%s " ,
gensec_security - > target . service ,
gensec_security - > target . hostname ) ;
} else {
principal = gensec_security - > target . hostname ;
2005-08-20 10:14:14 +04:00
}
2005-08-20 10:36:35 +04:00
2017-07-13 17:05:39 +03:00
DBG_PREFIX ( dbg_level , (
" %s: creating NEG_TOKEN_INIT for %s failed "
2017-07-13 17:26:42 +03:00
" (next[%s]): %s \n " , cur_sec - > op - > name ,
2017-07-13 17:08:05 +03:00
principal , next , nt_errstr ( status ) ) ) ;
2017-07-13 16:49:32 +03:00
2017-07-13 17:20:59 +03:00
if ( next = = NULL ) {
/*
* A hard error without a possible fallback .
*/
return status ;
}
2017-07-13 16:49:32 +03:00
/*
* Pretend we never started it
*/
2017-07-19 11:53:30 +03:00
gensec_spnego_reset_sub_sec ( spnego_state ) ;
2017-06-14 02:52:09 +03:00
/*
* And try the next one . . .
*/
n - > all_idx + = 1 ;
}
for ( ; n - > all_sec [ n - > all_idx ] . op ! = NULL ; n - > all_idx + + ) {
const struct gensec_security_ops_wrapper * cur_sec =
& n - > all_sec [ n - > all_idx ] ;
NTSTATUS status ;
status = gensec_subcontext_start ( spnego_state ,
gensec_security ,
& spnego_state - > sub_sec_security ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* select the sub context */
status = gensec_start_mech_by_ops ( spnego_state - > sub_sec_security ,
cur_sec - > op ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-07-19 11:53:30 +03:00
gensec_spnego_reset_sub_sec ( spnego_state ) ;
2017-06-14 02:52:09 +03:00
continue ;
}
/* In the client, try and produce the first (optimistic) packet */
if ( spnego_state - > state_position = = SPNEGO_CLIENT_START ) {
* in_next = data_blob_null ;
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
}
* in_next = data_blob_null ;
return NT_STATUS_OK ;
2017-07-13 16:41:23 +03:00
}
2008-08-12 16:26:21 +04:00
2017-07-13 17:20:59 +03:00
DBG_WARNING ( " Failed to setup SPNEGO negTokenInit request \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
2017-06-14 02:52:09 +03:00
}
static NTSTATUS gensec_spnego_create_negTokenInit_finish (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS sub_status ,
const DATA_BLOB sub_out ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * out )
{
const struct gensec_security_ops_wrapper * cur_sec =
& n - > all_sec [ n - > all_idx ] ;
struct spnego_data spnego_out ;
bool ok ;
2008-08-12 16:26:21 +04:00
2017-07-13 16:41:23 +03:00
spnego_out . type = SPNEGO_NEG_TOKEN_INIT ;
2008-08-12 16:26:21 +04:00
2017-06-14 02:52:09 +03:00
n - > mech_types = gensec_security_oids_from_ops_wrapped ( n , cur_sec ) ;
if ( n - > mech_types = = NULL ) {
2017-07-13 17:16:35 +03:00
DBG_WARNING ( " gensec_security_oids_from_ops_wrapped() failed \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
2012-09-18 21:34:48 +04:00
2017-07-13 16:41:23 +03:00
ok = spnego_write_mech_types ( spnego_state ,
2017-06-14 02:52:09 +03:00
n - > mech_types ,
2017-07-13 16:41:23 +03:00
& spnego_state - > mech_types ) ;
if ( ! ok ) {
2017-07-13 17:05:39 +03:00
DBG_ERR ( " Failed to write mechTypes \n " ) ;
2017-07-13 16:41:23 +03:00
return NT_STATUS_NO_MEMORY ;
}
2005-10-20 14:25:51 +04:00
2017-07-13 16:41:23 +03:00
/* List the remaining mechs as options */
2017-06-14 02:52:09 +03:00
spnego_out . negTokenInit . mechTypes = n - > mech_types ;
2017-07-13 16:41:23 +03:00
spnego_out . negTokenInit . reqFlags = data_blob_null ;
spnego_out . negTokenInit . reqFlagsPadding = 0 ;
2012-09-18 21:34:48 +04:00
2017-07-13 16:41:23 +03:00
if ( spnego_state - > state_position = = SPNEGO_SERVER_START ) {
spnego_out . negTokenInit . mechListMIC
= data_blob_string_const ( ADS_IGNORE_PRINCIPAL ) ;
} else {
spnego_out . negTokenInit . mechListMIC = data_blob_null ;
}
2012-09-18 21:34:48 +04:00
2017-06-14 02:52:09 +03:00
spnego_out . negTokenInit . mechToken = sub_out ;
2012-09-18 21:34:48 +04:00
2017-07-13 16:41:23 +03:00
if ( spnego_write_data ( out_mem_ctx , out , & spnego_out ) = = - 1 ) {
2017-07-13 17:05:39 +03:00
DBG_ERR ( " Failed to write NEG_TOKEN_INIT \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
2017-07-13 16:41:23 +03:00
}
2017-06-29 17:55:09 +03:00
2017-07-13 17:26:42 +03:00
/*
* Note that ' cur_sec ' is temporary memory , but
* cur_sec - > oid points to a const string in the
* backends gensec_security_ops structure .
*/
spnego_state - > neg_oid = cur_sec - > oid ;
2017-07-13 16:41:23 +03:00
2017-07-13 17:26:42 +03:00
/* set next state */
2017-07-13 16:41:23 +03:00
if ( spnego_state - > state_position = = SPNEGO_SERVER_START ) {
spnego_state - > state_position = SPNEGO_SERVER_START ;
spnego_state - > expected_packet = SPNEGO_NEG_TOKEN_INIT ;
} else {
spnego_state - > state_position = SPNEGO_CLIENT_TARG ;
spnego_state - > expected_packet = SPNEGO_NEG_TOKEN_TARG ;
2017-05-10 15:44:48 +03:00
}
2004-07-06 22:07:00 +04:00
2017-07-13 16:41:23 +03:00
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
2004-07-06 22:07:00 +04:00
}
2017-06-14 02:52:09 +03:00
static const struct spnego_neg_ops gensec_spnego_create_negTokenInit_ops = {
. name = " create_negTokenInit " ,
. start_fn = gensec_spnego_create_negTokenInit_start ,
. step_fn = gensec_spnego_create_negTokenInit_step ,
. finish_fn = gensec_spnego_create_negTokenInit_finish ,
} ;
2017-06-14 13:59:43 +03:00
static NTSTATUS gensec_spnego_client_negTokenInit_start (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
2017-07-07 09:11:32 +03:00
{
const char * tp = NULL ;
/* The server offers a list of mechanisms */
tp = spnego_in - > negTokenInit . targetPrincipal ;
if ( tp ! = NULL & & strcmp ( tp , ADS_IGNORE_PRINCIPAL ) ! = 0 ) {
DBG_INFO ( " Server claims it's principal name is %s \n " , tp ) ;
if ( lpcfg_client_use_spnego_principal ( gensec_security - > settings - > lp_ctx ) ) {
gensec_set_target_principal ( gensec_security , tp ) ;
}
}
2017-06-14 13:59:43 +03:00
n - > mech_idx = 0 ;
2019-10-03 13:09:29 +03:00
/* Do not use server mech list as it isn't protected. Instead, get all
* supported mechs ( excluding SPNEGO ) . */
n - > mech_types = gensec_security_oids ( gensec_security , n ,
GENSEC_OID_SPNEGO ) ;
2017-06-14 13:59:43 +03:00
if ( n - > mech_types = = NULL ) {
2017-07-07 11:11:43 +03:00
return NT_STATUS_INVALID_PARAMETER ;
}
2017-06-14 13:59:43 +03:00
n - > all_idx = 0 ;
n - > all_sec = gensec_security_by_oid_list ( gensec_security ,
n , n - > mech_types ,
GENSEC_OID_SPNEGO ) ;
if ( n - > all_sec = = NULL ) {
2017-07-07 11:11:43 +03:00
DBG_WARNING ( " gensec_security_by_oid_list() failed \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2017-06-14 13:59:43 +03:00
return n - > ops - > step_fn ( gensec_security , spnego_state , n ,
spnego_in , NT_STATUS_OK , in_mem_ctx , in_next ) ;
}
static NTSTATUS gensec_spnego_client_negTokenInit_step (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS last_status ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
{
if ( ! NT_STATUS_IS_OK ( last_status ) ) {
2017-07-07 11:11:43 +03:00
const struct gensec_security_ops_wrapper * cur_sec =
2017-06-14 13:59:43 +03:00
& n - > all_sec [ n - > all_idx ] ;
const struct gensec_security_ops_wrapper * next_sec = NULL ;
2017-07-07 11:11:43 +03:00
const char * next = NULL ;
const char * principal = NULL ;
int dbg_level = DBGLVL_WARNING ;
bool allow_fallback = false ;
2017-06-14 13:59:43 +03:00
NTSTATUS status = last_status ;
2017-07-07 11:11:43 +03:00
2017-06-14 13:59:43 +03:00
if ( cur_sec [ 1 ] . op ! = NULL ) {
next_sec = & cur_sec [ 1 ] ;
2017-07-07 11:11:43 +03:00
}
/*
* it is likely that a NULL input token will
* not be liked by most server mechs , but if
* we are in the client , we want the first
* update packet to be able to abort the use
* of this mech
*/
if ( NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_PARAMETER ) | |
2019-11-19 14:10:11 +03:00
NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_ACCOUNT_NAME ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_COMPUTER_NAME ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_NO_SUCH_DOMAIN ) | |
2017-07-07 11:11:43 +03:00
NT_STATUS_EQUAL ( status , NT_STATUS_NO_LOGON_SERVERS ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_TIME_DIFFERENCE_AT_DC ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_CANT_ACCESS_DOMAIN_INFO ) )
{
allow_fallback = true ;
}
2017-06-14 13:59:43 +03:00
if ( allow_fallback & & next_sec ! = NULL ) {
next = next_sec - > op - > name ;
2017-07-07 11:11:43 +03:00
dbg_level = DBGLVL_NOTICE ;
}
if ( gensec_security - > target . principal ! = NULL ) {
principal = gensec_security - > target . principal ;
} else if ( gensec_security - > target . service ! = NULL & &
gensec_security - > target . hostname ! = NULL )
{
principal = talloc_asprintf ( spnego_state - > sub_sec_security ,
" %s/%s " ,
gensec_security - > target . service ,
gensec_security - > target . hostname ) ;
} else {
principal = gensec_security - > target . hostname ;
}
DBG_PREFIX ( dbg_level , (
2017-06-14 13:59:43 +03:00
" %s: creating NEG_TOKEN_INIT for %s failed "
" (next[%s]): %s \n " , cur_sec - > op - > name ,
2017-07-07 11:11:43 +03:00
principal , next , nt_errstr ( status ) ) ) ;
2017-07-13 17:49:57 +03:00
if ( next = = NULL ) {
2017-07-07 11:11:43 +03:00
/*
2017-07-13 17:49:57 +03:00
* A hard error without a possible fallback .
2017-07-07 11:11:43 +03:00
*/
2017-07-13 17:49:57 +03:00
return status ;
2017-07-07 11:11:43 +03:00
}
/*
2017-07-13 17:49:57 +03:00
* Pretend we never started it .
2017-07-07 11:11:43 +03:00
*/
2017-07-19 11:53:30 +03:00
gensec_spnego_reset_sub_sec ( spnego_state ) ;
2017-06-14 13:59:43 +03:00
/*
* And try the next one . . .
*/
n - > all_idx + = 1 ;
}
for ( ; n - > all_sec [ n - > all_idx ] . op ! = NULL ; n - > all_idx + + ) {
const struct gensec_security_ops_wrapper * cur_sec =
& n - > all_sec [ n - > all_idx ] ;
NTSTATUS status ;
status = gensec_subcontext_start ( spnego_state ,
gensec_security ,
& spnego_state - > sub_sec_security ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* select the sub context */
status = gensec_start_mech_by_ops ( spnego_state - > sub_sec_security ,
cur_sec - > op ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-07-19 11:53:30 +03:00
gensec_spnego_reset_sub_sec ( spnego_state ) ;
2017-06-14 13:59:43 +03:00
continue ;
}
/*
* Note that ' cur_sec ' is temporary memory , but
* cur_sec - > oid points to a const string in the
* backends gensec_security_ops structure .
*/
spnego_state - > neg_oid = cur_sec - > oid ;
/*
* As client we don ' t use an optimistic token from the server .
* But try to produce one for the server .
*/
* in_next = data_blob_null ;
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
2017-07-07 09:11:32 +03:00
}
2017-07-07 11:11:43 +03:00
DBG_WARNING ( " Could not find a suitable mechtype in NEG_TOKEN_INIT \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
2017-06-14 13:59:43 +03:00
}
static NTSTATUS gensec_spnego_client_negTokenInit_finish (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS sub_status ,
const DATA_BLOB sub_out ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * out )
{
struct spnego_data spnego_out ;
2019-10-03 13:09:29 +03:00
const char * const * mech_types = NULL ;
2017-06-14 13:59:43 +03:00
bool ok ;
2017-07-07 11:11:43 +03:00
2019-10-03 13:09:29 +03:00
if ( n - > mech_types = = NULL ) {
DBG_WARNING ( " No mech_types list \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
for ( mech_types = n - > mech_types ; * mech_types ! = NULL ; mech_types + + ) {
int cmp = strcmp ( * mech_types , spnego_state - > neg_oid ) ;
if ( cmp = = 0 ) {
break ;
}
}
if ( * mech_types = = NULL ) {
DBG_ERR ( " Can't find selected sub mechanism in mech_types \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2017-07-07 09:11:32 +03:00
/* compose reply */
spnego_out . type = SPNEGO_NEG_TOKEN_INIT ;
2019-10-03 13:09:29 +03:00
spnego_out . negTokenInit . mechTypes = mech_types ;
2017-07-07 09:11:32 +03:00
spnego_out . negTokenInit . reqFlags = data_blob_null ;
spnego_out . negTokenInit . reqFlagsPadding = 0 ;
spnego_out . negTokenInit . mechListMIC = data_blob_null ;
spnego_out . negTokenInit . mechToken = sub_out ;
if ( spnego_write_data ( out_mem_ctx , out , & spnego_out ) = = - 1 ) {
DBG_ERR ( " Failed to write SPNEGO reply to NEG_TOKEN_INIT \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
ok = spnego_write_mech_types ( spnego_state ,
2019-10-03 13:09:29 +03:00
mech_types ,
2017-07-07 09:11:32 +03:00
& spnego_state - > mech_types ) ;
if ( ! ok ) {
DBG_ERR ( " failed to write mechTypes \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
/* set next state */
spnego_state - > expected_packet = SPNEGO_NEG_TOKEN_TARG ;
spnego_state - > state_position = SPNEGO_CLIENT_TARG ;
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
}
2004-08-11 22:09:40 +04:00
2017-06-14 13:59:43 +03:00
static const struct spnego_neg_ops gensec_spnego_client_negTokenInit_ops = {
. name = " client_negTokenInit " ,
. start_fn = gensec_spnego_client_negTokenInit_start ,
. step_fn = gensec_spnego_client_negTokenInit_step ,
. finish_fn = gensec_spnego_client_negTokenInit_finish ,
} ;
2017-06-14 14:56:02 +03:00
static NTSTATUS gensec_spnego_client_negTokenTarg_start (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
2017-07-07 10:01:18 +03:00
{
struct spnego_negTokenTarg * ta = & spnego_in - > negTokenTarg ;
NTSTATUS status ;
spnego_state - > num_targs + + ;
if ( ta - > negResult = = SPNEGO_REJECT ) {
return NT_STATUS_LOGON_FAILURE ;
}
if ( ta - > negResult = = SPNEGO_REQUEST_MIC ) {
spnego_state - > mic_requested = true ;
}
if ( ta - > mechListMIC . length > 0 ) {
DATA_BLOB * m = & ta - > mechListMIC ;
const DATA_BLOB * r = & ta - > responseToken ;
/*
* Windows 2000 has a bug , it repeats the
* responseToken in the mechListMIC field .
*/
if ( m - > length = = r - > length ) {
int cmp ;
cmp = memcmp ( m - > data , r - > data , m - > length ) ;
if ( cmp = = 0 ) {
data_blob_free ( m ) ;
}
}
}
/* Server didn't like our choice of mech, and chose something else */
if ( ( ( ta - > negResult = = SPNEGO_ACCEPT_INCOMPLETE ) | |
( ta - > negResult = = SPNEGO_REQUEST_MIC ) ) & &
ta - > supportedMech ! = NULL & &
strcmp ( ta - > supportedMech , spnego_state - > neg_oid ) ! = 0 )
{
const char * client_mech = NULL ;
const char * client_oid = NULL ;
const char * server_mech = NULL ;
const char * server_oid = NULL ;
client_mech = gensec_get_name_by_oid ( gensec_security ,
spnego_state - > neg_oid ) ;
client_oid = spnego_state - > neg_oid ;
server_mech = gensec_get_name_by_oid ( gensec_security ,
ta - > supportedMech ) ;
server_oid = ta - > supportedMech ;
DBG_NOTICE ( " client preferred mech (%s[%s]) not accepted, "
" server wants: %s[%s] \n " ,
client_mech , client_oid , server_mech , server_oid ) ;
spnego_state - > downgraded = true ;
2017-07-19 11:53:30 +03:00
gensec_spnego_reset_sub_sec ( spnego_state ) ;
2017-07-07 10:01:18 +03:00
status = gensec_subcontext_start ( spnego_state ,
gensec_security ,
& spnego_state - > sub_sec_security ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* select the sub context */
status = gensec_start_mech_by_oid ( spnego_state - > sub_sec_security ,
ta - > supportedMech ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
spnego_state - > neg_oid = talloc_strdup ( spnego_state ,
ta - > supportedMech ) ;
if ( spnego_state - > neg_oid = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
}
if ( ta - > mechListMIC . length > 0 ) {
if ( spnego_state - > sub_sec_ready ) {
spnego_state - > needs_mic_check = true ;
}
}
if ( spnego_state - > needs_mic_check ) {
if ( ta - > responseToken . length ! = 0 ) {
DBG_WARNING ( " non empty response token not expected \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( ta - > mechListMIC . length = = 0
& & spnego_state - > may_skip_mic_check ) {
/*
* In this case we don ' t require
* a mechListMIC from the server .
*
* This works around bugs in the Azure
* and Apple spnego implementations .
*
* See
* https : //bugzilla.samba.org/show_bug.cgi?id=11994
*/
spnego_state - > needs_mic_check = false ;
2017-06-14 14:56:02 +03:00
return NT_STATUS_OK ;
2017-07-07 10:01:18 +03:00
}
status = gensec_check_packet ( spnego_state - > sub_sec_security ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
& ta - > mechListMIC ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " failed to verify mechListMIC: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
spnego_state - > needs_mic_check = false ;
spnego_state - > done_mic_check = true ;
2017-06-14 14:56:02 +03:00
return NT_STATUS_OK ;
2017-07-07 10:01:18 +03:00
}
if ( ! spnego_state - > sub_sec_ready ) {
2017-06-14 14:56:02 +03:00
* in_next = ta - > responseToken ;
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
}
return NT_STATUS_OK ;
}
static NTSTATUS gensec_spnego_client_negTokenTarg_step (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS last_status ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
{
if ( GENSEC_UPDATE_IS_NTERROR ( last_status ) ) {
DBG_WARNING ( " SPNEGO(%s) login failed: %s \n " ,
spnego_state - > sub_sec_security - > ops - > name ,
nt_errstr ( last_status ) ) ;
return last_status ;
}
/*
* This should never be reached !
* The step function is only called on errors !
*/
smb_panic ( __location__ ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
static NTSTATUS gensec_spnego_client_negTokenTarg_finish (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS sub_status ,
const DATA_BLOB sub_out ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * out )
{
const struct spnego_negTokenTarg * ta =
& spnego_in - > negTokenTarg ;
DATA_BLOB mech_list_mic = data_blob_null ;
NTSTATUS status ;
struct spnego_data spnego_out ;
if ( ! spnego_state - > sub_sec_ready ) {
/*
* We ' re not yet ready to deal with signatures .
*/
goto client_response ;
}
if ( spnego_state - > done_mic_check ) {
/*
* We already checked the mic ,
* either the in last round here
* in gensec_spnego_client_negTokenTarg_finish ( )
* or during this round in
* gensec_spnego_client_negTokenTarg_start ( ) .
*
* Both cases we ' re sure we don ' t have to
* call gensec_sign_packet ( ) .
*/
goto client_response ;
}
if ( spnego_state - > may_skip_mic_check ) {
/*
* This can only be set during
* the last round here in
* gensec_spnego_client_negTokenTarg_finish ( )
* below . And during this round
* we already passed the checks in
* gensec_spnego_client_negTokenTarg_start ( ) .
*
* So we need to skip to deal with
* any signatures now .
*/
goto client_response ;
2017-07-07 10:01:18 +03:00
}
if ( ! spnego_state - > done_mic_check ) {
bool have_sign = true ;
bool new_spnego = false ;
have_sign = gensec_have_feature ( spnego_state - > sub_sec_security ,
GENSEC_FEATURE_SIGN ) ;
if ( spnego_state - > simulate_w2k ) {
have_sign = false ;
}
new_spnego = gensec_have_feature ( spnego_state - > sub_sec_security ,
GENSEC_FEATURE_NEW_SPNEGO ) ;
switch ( ta - > negResult ) {
case SPNEGO_ACCEPT_COMPLETED :
case SPNEGO_NONE_RESULT :
if ( spnego_state - > num_targs = = 1 ) {
/*
* the first exchange doesn ' t require
* verification
*/
new_spnego = false ;
}
break ;
case SPNEGO_ACCEPT_INCOMPLETE :
if ( ta - > mechListMIC . length > 0 ) {
new_spnego = true ;
break ;
}
if ( spnego_state - > downgraded ) {
/*
* A downgrade should be protected if
* supported
*/
break ;
}
/*
* The caller may just asked for
* GENSEC_FEATURE_SESSION_KEY , this
* is only reflected in the want_features .
*
* As it will imply
* gensec_have_features ( GENSEC_FEATURE_SIGN )
* to return true .
*/
if ( gensec_security - > want_features & GENSEC_FEATURE_SIGN ) {
break ;
}
if ( gensec_security - > want_features & GENSEC_FEATURE_SEAL ) {
break ;
}
/*
* Here we ' re sure our preferred mech was
* selected by the server and our caller doesn ' t
* need GENSEC_FEATURE_SIGN nor
* GENSEC_FEATURE_SEAL support .
*
* In this case we don ' t require
* a mechListMIC from the server .
*
* This works around bugs in the Azure
* and Apple spnego implementations .
*
* See
* https : //bugzilla.samba.org/show_bug.cgi?id=11994
*/
spnego_state - > may_skip_mic_check = true ;
break ;
case SPNEGO_REQUEST_MIC :
if ( ta - > mechListMIC . length > 0 ) {
new_spnego = true ;
}
break ;
default :
break ;
}
if ( spnego_state - > mic_requested ) {
if ( have_sign ) {
new_spnego = true ;
}
}
if ( have_sign & & new_spnego ) {
spnego_state - > needs_mic_check = true ;
spnego_state - > needs_mic_sign = true ;
}
}
if ( ta - > mechListMIC . length > 0 ) {
status = gensec_check_packet ( spnego_state - > sub_sec_security ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
& ta - > mechListMIC ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " failed to verify mechListMIC: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
spnego_state - > needs_mic_check = false ;
spnego_state - > done_mic_check = true ;
}
if ( spnego_state - > needs_mic_sign ) {
status = gensec_sign_packet ( spnego_state - > sub_sec_security ,
2017-06-14 14:56:02 +03:00
n ,
2017-07-07 10:01:18 +03:00
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
& mech_list_mic ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " failed to sign mechListMIC: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
spnego_state - > needs_mic_sign = false ;
}
client_response :
2017-07-17 21:47:57 +03:00
if ( sub_out . length = = 0 & & mech_list_mic . length = = 0 ) {
2017-07-07 10:01:18 +03:00
* out = data_blob_null ;
2017-07-17 21:49:34 +03:00
if ( ! spnego_state - > sub_sec_ready ) {
/* somethings wrong here... */
DBG_ERR ( " gensec_update not ready without output \n " ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
2017-07-07 10:01:18 +03:00
if ( ta - > negResult ! = SPNEGO_ACCEPT_COMPLETED ) {
/* unless of course it did not accept */
DBG_WARNING ( " gensec_update ok but not accepted \n " ) ;
2017-07-17 21:47:57 +03:00
return NT_STATUS_INVALID_PARAMETER ;
2017-07-07 10:01:18 +03:00
}
2017-07-18 12:42:43 +03:00
if ( ! spnego_state - > needs_mic_check ) {
spnego_state - > state_position = SPNEGO_DONE ;
return NT_STATUS_OK ;
}
2017-07-17 21:47:57 +03:00
}
/* compose reply */
spnego_out . type = SPNEGO_NEG_TOKEN_TARG ;
spnego_out . negTokenTarg . negResult = SPNEGO_NONE_RESULT ;
spnego_out . negTokenTarg . supportedMech = NULL ;
spnego_out . negTokenTarg . responseToken = sub_out ;
spnego_out . negTokenTarg . mechListMIC = mech_list_mic ;
if ( spnego_write_data ( out_mem_ctx , out , & spnego_out ) = = - 1 ) {
DBG_WARNING ( " Failed to write NEG_TOKEN_TARG \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
2017-07-07 10:01:18 +03:00
}
2017-07-17 21:47:57 +03:00
spnego_state - > num_targs + + ;
/* set next state */
spnego_state - > state_position = SPNEGO_CLIENT_TARG ;
spnego_state - > expected_packet = SPNEGO_NEG_TOKEN_TARG ;
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
2017-07-07 10:01:18 +03:00
}
2017-06-14 14:56:02 +03:00
static const struct spnego_neg_ops gensec_spnego_client_negTokenTarg_ops = {
. name = " client_negTokenTarg " ,
. start_fn = gensec_spnego_client_negTokenTarg_start ,
. step_fn = gensec_spnego_client_negTokenTarg_step ,
. finish_fn = gensec_spnego_client_negTokenTarg_finish ,
} ;
2005-10-20 07:20:43 +04:00
/** create a server negTokenTarg
2004-08-11 22:09:40 +04:00
*
* This is the case , where the client is the first one who sends data
*/
2017-06-30 12:00:12 +03:00
static NTSTATUS gensec_spnego_server_response ( struct spnego_state * spnego_state ,
TALLOC_CTX * out_mem_ctx ,
NTSTATUS nt_status ,
const DATA_BLOB unwrapped_out ,
DATA_BLOB mech_list_mic ,
DATA_BLOB * out )
2004-08-11 22:09:40 +04:00
{
struct spnego_data spnego_out ;
/* compose reply */
spnego_out . type = SPNEGO_NEG_TOKEN_TARG ;
spnego_out . negTokenTarg . responseToken = unwrapped_out ;
2015-11-24 22:13:24 +03:00
spnego_out . negTokenTarg . mechListMIC = mech_list_mic ;
2004-12-02 21:15:39 +03:00
spnego_out . negTokenTarg . supportedMech = NULL ;
2005-05-16 03:42:11 +04:00
if ( NT_STATUS_EQUAL ( nt_status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
spnego_out . negTokenTarg . supportedMech = spnego_state - > neg_oid ;
2015-11-24 22:13:24 +03:00
if ( spnego_state - > mic_requested ) {
spnego_out . negTokenTarg . negResult = SPNEGO_REQUEST_MIC ;
spnego_state - > mic_requested = false ;
} else {
spnego_out . negTokenTarg . negResult = SPNEGO_ACCEPT_INCOMPLETE ;
}
2004-08-11 22:09:40 +04:00
spnego_state - > state_position = SPNEGO_SERVER_TARG ;
} else if ( NT_STATUS_IS_OK ( nt_status ) ) {
2004-12-02 21:15:39 +03:00
if ( unwrapped_out . data ) {
2005-05-16 03:42:11 +04:00
spnego_out . negTokenTarg . supportedMech = spnego_state - > neg_oid ;
2004-12-02 21:15:39 +03:00
}
2004-08-11 22:09:40 +04:00
spnego_out . negTokenTarg . negResult = SPNEGO_ACCEPT_COMPLETED ;
spnego_state - > state_position = SPNEGO_DONE ;
}
2004-12-02 21:15:39 +03:00
2004-08-11 22:09:40 +04:00
if ( spnego_write_data ( out_mem_ctx , out , & spnego_out ) = = - 1 ) {
DEBUG ( 1 , ( " Failed to write SPNEGO reply to NEG_TOKEN_TARG \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
spnego_state - > expected_packet = SPNEGO_NEG_TOKEN_TARG ;
2015-11-24 22:13:24 +03:00
spnego_state - > num_targs + + ;
2004-08-11 22:09:40 +04:00
return nt_status ;
}
2017-06-14 16:22:57 +03:00
static NTSTATUS gensec_spnego_server_negTokenInit_start (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
2017-07-07 09:30:24 +03:00
{
2017-07-07 12:39:39 +03:00
bool ok ;
2017-07-07 09:30:24 +03:00
2017-06-14 16:22:57 +03:00
n - > mech_idx = 0 ;
n - > mech_types = spnego_in - > negTokenInit . mechTypes ;
if ( n - > mech_types = = NULL ) {
2017-07-07 12:39:39 +03:00
return NT_STATUS_INVALID_PARAMETER ;
}
2017-07-07 09:30:24 +03:00
2017-06-14 16:22:57 +03:00
n - > all_idx = 0 ;
n - > all_sec = gensec_security_by_oid_list ( gensec_security ,
n , n - > mech_types ,
GENSEC_OID_SPNEGO ) ;
if ( n - > all_sec = = NULL ) {
2017-07-07 12:39:39 +03:00
DBG_WARNING ( " gensec_security_by_oid_list() failed \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2017-06-14 16:22:57 +03:00
ok = spnego_write_mech_types ( spnego_state ,
n - > mech_types ,
2017-07-07 12:39:39 +03:00
& spnego_state - > mech_types ) ;
if ( ! ok ) {
DBG_ERR ( " Failed to write mechTypes \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
2017-06-14 16:22:57 +03:00
return n - > ops - > step_fn ( gensec_security , spnego_state , n ,
spnego_in , NT_STATUS_OK , in_mem_ctx , in_next ) ;
}
static NTSTATUS gensec_spnego_server_negTokenInit_step (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS last_status ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
{
if ( ! NT_STATUS_IS_OK ( last_status ) ) {
const struct gensec_security_ops_wrapper * cur_sec =
& n - > all_sec [ n - > all_idx ] ;
const char * next_mech = n - > mech_types [ n - > mech_idx + 1 ] ;
const struct gensec_security_ops_wrapper * next_sec = NULL ;
const char * next = NULL ;
int dbg_level = DBGLVL_WARNING ;
bool allow_fallback = false ;
NTSTATUS status = last_status ;
size_t i ;
for ( i = 0 ; next_mech ! = NULL & & n - > all_sec [ i ] . op ! = NULL ; i + + ) {
if ( strcmp ( next_mech , n - > all_sec [ i ] . oid ) ! = 0 ) {
continue ;
}
next_sec = & n - > all_sec [ i ] ;
break ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_PARAMETER ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_CANT_ACCESS_DOMAIN_INFO ) )
{
allow_fallback = true ;
}
if ( allow_fallback & & next_sec ! = NULL ) {
next = next_sec - > op - > name ;
dbg_level = DBGLVL_NOTICE ;
}
DBG_PREFIX ( dbg_level , (
" %s: parsing NEG_TOKEN_INIT content failed "
" (next[%s]): %s \n " , cur_sec - > op - > name ,
next , nt_errstr ( status ) ) ) ;
if ( next = = NULL ) {
/*
* A hard error without a possible fallback .
*/
return status ;
}
/*
* Pretend we never started it
*/
2017-07-19 11:53:30 +03:00
gensec_spnego_reset_sub_sec ( spnego_state ) ;
2017-06-14 16:22:57 +03:00
/*
* And try the next one , based on the clients
* mech type list . . .
*/
n - > mech_idx + = 1 ;
}
2017-07-07 12:39:39 +03:00
/*
2017-06-14 16:22:57 +03:00
* we always reset all_idx here , as the negotiation is
* done via mech_idx !
2017-07-07 12:39:39 +03:00
*/
2017-06-14 16:22:57 +03:00
n - > all_idx = 0 ;
for ( ; n - > mech_types [ n - > mech_idx ] ! = NULL ; n - > mech_idx + + ) {
const char * cur_mech = n - > mech_types [ n - > mech_idx ] ;
2017-07-07 12:39:39 +03:00
const struct gensec_security_ops_wrapper * cur_sec = NULL ;
2017-06-14 16:22:57 +03:00
NTSTATUS status ;
2017-07-07 12:39:39 +03:00
DATA_BLOB sub_in = data_blob_null ;
2017-06-14 16:22:57 +03:00
size_t i ;
2017-07-07 12:39:39 +03:00
2017-06-14 16:22:57 +03:00
for ( i = 0 ; n - > all_sec [ i ] . op ! = NULL ; i + + ) {
if ( strcmp ( cur_mech , n - > all_sec [ i ] . oid ) ! = 0 ) {
continue ;
2017-07-07 12:39:39 +03:00
}
2017-06-14 16:22:57 +03:00
cur_sec = & n - > all_sec [ i ] ;
n - > all_idx = i ;
break ;
2017-07-07 12:39:39 +03:00
}
if ( cur_sec = = NULL ) {
continue ;
}
status = gensec_subcontext_start ( spnego_state ,
gensec_security ,
& spnego_state - > sub_sec_security ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* select the sub context */
status = gensec_start_mech_by_ops ( spnego_state - > sub_sec_security ,
cur_sec - > op ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
/*
* Pretend we never started it
*/
2017-07-19 11:53:30 +03:00
gensec_spnego_reset_sub_sec ( spnego_state ) ;
2017-07-07 12:39:39 +03:00
continue ;
}
2017-06-14 16:22:57 +03:00
if ( n - > mech_idx = = 0 ) {
/*
* We can use the optimistic token .
*/
sub_in = spnego_in - > negTokenInit . mechToken ;
} else {
2017-07-07 12:39:39 +03:00
/*
* Indicate the downgrade and request a
* mic .
*/
spnego_state - > downgraded = true ;
spnego_state - > mic_requested = true ;
}
2019-09-04 17:04:12 +03:00
if ( sub_in . length = = 0 ) {
spnego_state - > no_optimistic = true ;
}
2017-07-07 12:39:39 +03:00
/*
2017-06-14 16:22:57 +03:00
* Note that ' cur_sec ' is temporary memory , but
* cur_sec - > oid points to a const string in the
* backends gensec_security_ops structure .
2017-07-07 12:39:39 +03:00
*/
spnego_state - > neg_oid = cur_sec - > oid ;
2017-06-14 16:22:57 +03:00
/* we need some content from the mech */
* in_next = sub_in ;
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
2017-07-07 12:39:39 +03:00
}
DBG_WARNING ( " Could not find a suitable mechtype in NEG_TOKEN_INIT \n " ) ;
2017-07-17 22:54:51 +03:00
return NT_STATUS_INVALID_PARAMETER ;
2017-06-14 16:22:57 +03:00
}
static NTSTATUS gensec_spnego_server_negTokenInit_finish (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS sub_status ,
const DATA_BLOB sub_out ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * out )
{
DATA_BLOB mech_list_mic = data_blob_null ;
2017-07-07 12:39:39 +03:00
2017-07-07 09:30:24 +03:00
if ( spnego_state - > simulate_w2k ) {
/*
* Windows 2000 returns the unwrapped token
* also in the mech_list_mic field .
*
* In order to verify our client code ,
* we need a way to have a server with this
* broken behaviour
*/
mech_list_mic = sub_out ;
}
2017-06-14 16:22:57 +03:00
return gensec_spnego_server_response ( spnego_state ,
out_mem_ctx ,
sub_status ,
sub_out ,
mech_list_mic ,
out ) ;
}
static const struct spnego_neg_ops gensec_spnego_server_negTokenInit_ops = {
. name = " server_negTokenInit " ,
. start_fn = gensec_spnego_server_negTokenInit_start ,
. step_fn = gensec_spnego_server_negTokenInit_step ,
. finish_fn = gensec_spnego_server_negTokenInit_finish ,
} ;
2017-06-14 16:40:41 +03:00
static NTSTATUS gensec_spnego_server_negTokenTarg_start (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
2017-07-07 10:18:18 +03:00
{
const struct spnego_negTokenTarg * ta = & spnego_in - > negTokenTarg ;
NTSTATUS status ;
spnego_state - > num_targs + + ;
if ( spnego_state - > sub_sec_security = = NULL ) {
DBG_ERR ( " SPNEGO: Did not setup a mech in NEG_TOKEN_INIT \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( spnego_state - > needs_mic_check ) {
if ( ta - > responseToken . length ! = 0 ) {
DBG_WARNING ( " non empty response token not expected \n " ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
status = gensec_check_packet ( spnego_state - > sub_sec_security ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
& ta - > mechListMIC ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " failed to verify mechListMIC: %s \n " ,
nt_errstr ( status ) ) ;
2017-07-17 22:54:51 +03:00
return status ;
2017-07-07 10:18:18 +03:00
}
spnego_state - > needs_mic_check = false ;
spnego_state - > done_mic_check = true ;
2017-06-14 16:40:41 +03:00
return NT_STATUS_OK ;
2017-07-07 10:18:18 +03:00
}
if ( ! spnego_state - > sub_sec_ready ) {
2017-06-14 16:40:41 +03:00
* in_next = ta - > responseToken ;
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
}
return NT_STATUS_OK ;
}
static NTSTATUS gensec_spnego_server_negTokenTarg_step (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS last_status ,
TALLOC_CTX * in_mem_ctx ,
DATA_BLOB * in_next )
{
if ( GENSEC_UPDATE_IS_NTERROR ( last_status ) ) {
DBG_NOTICE ( " SPNEGO(%s) login failed: %s \n " ,
spnego_state - > sub_sec_security - > ops - > name ,
nt_errstr ( last_status ) ) ;
return last_status ;
}
/*
* This should never be reached !
* The step function is only called on errors !
*/
smb_panic ( __location__ ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
static NTSTATUS gensec_spnego_server_negTokenTarg_finish (
struct gensec_security * gensec_security ,
struct spnego_state * spnego_state ,
struct spnego_neg_state * n ,
struct spnego_data * spnego_in ,
NTSTATUS sub_status ,
const DATA_BLOB sub_out ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * out )
{
const struct spnego_negTokenTarg * ta = & spnego_in - > negTokenTarg ;
DATA_BLOB mech_list_mic = data_blob_null ;
NTSTATUS status ;
bool have_sign = true ;
bool new_spnego = false ;
status = sub_status ;
if ( ! spnego_state - > sub_sec_ready ) {
/*
* We ' re not yet ready to deal with signatures .
*/
goto server_response ;
}
if ( spnego_state - > done_mic_check ) {
/*
* We already checked the mic ,
* either the in last round here
* in gensec_spnego_server_negTokenTarg_finish ( )
* or during this round in
* gensec_spnego_server_negTokenTarg_start ( ) .
*
* Both cases we ' re sure we don ' t have to
* call gensec_sign_packet ( ) .
*/
goto server_response ;
2017-07-07 10:18:18 +03:00
}
have_sign = gensec_have_feature ( spnego_state - > sub_sec_security ,
GENSEC_FEATURE_SIGN ) ;
if ( spnego_state - > simulate_w2k ) {
have_sign = false ;
}
new_spnego = gensec_have_feature ( spnego_state - > sub_sec_security ,
GENSEC_FEATURE_NEW_SPNEGO ) ;
if ( ta - > mechListMIC . length > 0 ) {
new_spnego = true ;
}
if ( have_sign & & new_spnego ) {
spnego_state - > needs_mic_check = true ;
spnego_state - > needs_mic_sign = true ;
}
if ( have_sign & & ta - > mechListMIC . length > 0 ) {
status = gensec_check_packet ( spnego_state - > sub_sec_security ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
& ta - > mechListMIC ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " failed to verify mechListMIC: %s \n " ,
nt_errstr ( status ) ) ;
2017-07-17 22:54:51 +03:00
return status ;
2017-07-07 10:18:18 +03:00
}
spnego_state - > needs_mic_check = false ;
spnego_state - > done_mic_check = true ;
}
if ( spnego_state - > needs_mic_sign ) {
status = gensec_sign_packet ( spnego_state - > sub_sec_security ,
2017-06-14 16:40:41 +03:00
n ,
2017-07-07 10:18:18 +03:00
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
spnego_state - > mech_types . data ,
spnego_state - > mech_types . length ,
& mech_list_mic ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " failed to sign mechListMIC: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
spnego_state - > needs_mic_sign = false ;
}
if ( spnego_state - > needs_mic_check ) {
status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
}
server_response :
return gensec_spnego_server_response ( spnego_state ,
out_mem_ctx ,
status ,
sub_out ,
mech_list_mic ,
out ) ;
}
2017-06-14 16:40:41 +03:00
static const struct spnego_neg_ops gensec_spnego_server_negTokenTarg_ops = {
. name = " server_negTokenTarg " ,
. start_fn = gensec_spnego_server_negTokenTarg_start ,
. step_fn = gensec_spnego_server_negTokenTarg_step ,
. finish_fn = gensec_spnego_server_negTokenTarg_finish ,
} ;
2017-06-13 17:53:06 +03:00
struct gensec_spnego_update_state {
2017-06-14 00:43:01 +03:00
struct tevent_context * ev ;
2017-06-13 17:53:06 +03:00
struct gensec_security * gensec ;
struct spnego_state * spnego ;
2017-06-14 00:43:01 +03:00
2017-06-13 17:53:06 +03:00
DATA_BLOB full_in ;
2017-06-14 04:39:02 +03:00
struct spnego_data _spnego_in ;
struct spnego_data * spnego_in ;
2017-06-14 00:43:01 +03:00
struct {
bool needed ;
DATA_BLOB in ;
NTSTATUS status ;
DATA_BLOB out ;
} sub ;
2017-06-14 12:01:23 +03:00
struct spnego_neg_state * n ;
2017-06-13 17:53:06 +03:00
NTSTATUS status ;
DATA_BLOB out ;
} ;
2017-06-14 09:43:13 +03:00
static void gensec_spnego_update_cleanup ( struct tevent_req * req ,
enum tevent_req_state req_state )
{
struct gensec_spnego_update_state * state =
tevent_req_data ( req ,
struct gensec_spnego_update_state ) ;
switch ( req_state ) {
case TEVENT_REQ_USER_ERROR :
case TEVENT_REQ_TIMED_OUT :
case TEVENT_REQ_NO_MEMORY :
/*
* A fatal error , further updates are not allowed .
*/
state - > spnego - > state_position = SPNEGO_DONE ;
break ;
default :
break ;
}
}
2017-06-13 17:59:02 +03:00
static NTSTATUS gensec_spnego_update_in ( struct gensec_security * gensec_security ,
2017-06-13 23:41:14 +03:00
const DATA_BLOB in , TALLOC_CTX * mem_ctx ,
DATA_BLOB * full_in ) ;
2017-06-14 00:43:01 +03:00
static void gensec_spnego_update_pre ( struct tevent_req * req ) ;
2017-06-14 00:43:01 +03:00
static void gensec_spnego_update_done ( struct tevent_req * subreq ) ;
2017-06-14 00:43:01 +03:00
static void gensec_spnego_update_post ( struct tevent_req * req ) ;
2017-06-13 23:43:59 +03:00
static NTSTATUS gensec_spnego_update_out ( struct gensec_security * gensec_security ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * _out ) ;
2017-06-13 17:59:02 +03:00
2017-06-13 17:53:06 +03:00
static struct tevent_req * gensec_spnego_update_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct gensec_security * gensec_security ,
const DATA_BLOB in )
2011-12-24 03:27:45 +04:00
{
2017-06-13 17:53:06 +03:00
struct spnego_state * spnego_state =
talloc_get_type_abort ( gensec_security - > private_data ,
struct spnego_state ) ;
struct tevent_req * req = NULL ;
struct gensec_spnego_update_state * state = NULL ;
2011-12-24 03:27:45 +04:00
NTSTATUS status ;
2017-06-14 04:39:02 +03:00
ssize_t len ;
2011-12-24 03:27:45 +04:00
2017-06-13 17:53:06 +03:00
req = tevent_req_create ( mem_ctx , & state ,
struct gensec_spnego_update_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2017-06-14 00:43:01 +03:00
state - > ev = ev ;
2017-06-13 17:53:06 +03:00
state - > gensec = gensec_security ;
state - > spnego = spnego_state ;
2017-06-14 09:43:13 +03:00
tevent_req_set_cleanup_fn ( req , gensec_spnego_update_cleanup ) ;
2011-12-24 03:27:45 +04:00
if ( spnego_state - > out_frag . length > 0 ) {
if ( in . length > 0 ) {
2017-06-13 17:53:06 +03:00
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
2011-12-24 03:27:45 +04:00
}
2017-06-13 17:53:06 +03:00
status = gensec_spnego_update_out ( gensec_security ,
state , & state - > out ) ;
2017-06-28 15:53:49 +03:00
if ( GENSEC_UPDATE_IS_NTERROR ( status ) ) {
tevent_req_nterror ( req , status ) ;
2017-06-13 17:53:06 +03:00
return tevent_req_post ( req , ev ) ;
}
2017-06-28 15:53:49 +03:00
state - > status = status ;
2017-06-13 17:53:06 +03:00
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
2011-12-24 03:27:45 +04:00
}
2017-06-13 23:41:14 +03:00
status = gensec_spnego_update_in ( gensec_security , in ,
state , & state - > full_in ) ;
2017-06-13 17:53:06 +03:00
state - > status = status ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
2011-12-24 03:27:45 +04:00
}
2017-06-14 04:39:02 +03:00
/* Check if we got a valid SPNEGO blob... */
switch ( spnego_state - > state_position ) {
case SPNEGO_FALLBACK :
break ;
case SPNEGO_CLIENT_TARG :
case SPNEGO_SERVER_TARG :
if ( state - > full_in . length = = 0 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
}
2017-07-26 19:24:26 +03:00
FALL_THROUGH ;
2017-06-14 04:39:02 +03:00
case SPNEGO_CLIENT_START :
case SPNEGO_SERVER_START :
if ( state - > full_in . length = = 0 ) {
/* create_negTokenInit later */
break ;
}
len = spnego_read_data ( state ,
state - > full_in ,
& state - > _spnego_in ) ;
if ( len = = - 1 ) {
if ( spnego_state - > state_position ! = SPNEGO_SERVER_START ) {
DEBUG ( 1 , ( " Invalid SPNEGO request: \n " ) ) ;
dump_data ( 1 , state - > full_in . data ,
state - > full_in . length ) ;
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
}
/*
* This is the ' fallback ' case , where we don ' t get
* SPNEGO , and have to try all the other options ( and
* hope they all have a magic string they check )
*/
status = gensec_spnego_server_try_fallback ( gensec_security ,
spnego_state ,
state ,
state - > full_in ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
/*
* We ' ll continue with SPNEGO_FALLBACK below . . .
*/
break ;
}
state - > spnego_in = & state - > _spnego_in ;
/* OK, so it's real SPNEGO, check the packet's the one we expect */
if ( state - > spnego_in - > type ! = spnego_state - > expected_packet ) {
DEBUG ( 1 , ( " Invalid SPNEGO request: %d, expected %d \n " ,
state - > spnego_in - > type ,
spnego_state - > expected_packet ) ) ;
dump_data ( 1 , state - > full_in . data ,
state - > full_in . length ) ;
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
}
break ;
default :
smb_panic ( __location__ ) ;
return NULL ;
}
2017-06-14 00:43:01 +03:00
gensec_spnego_update_pre ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
2017-06-28 15:53:49 +03:00
return tevent_req_post ( req , ev ) ;
}
2017-06-14 00:43:01 +03:00
if ( state - > sub . needed ) {
struct tevent_req * subreq = NULL ;
/*
* We may need one more roundtrip . . .
*/
subreq = gensec_update_send ( state , state - > ev ,
spnego_state - > sub_sec_security ,
state - > sub . in ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
gensec_spnego_update_done ,
req ) ;
state - > sub . needed = false ;
return req ;
}
2011-12-24 03:27:45 +04:00
2017-06-14 00:43:01 +03:00
gensec_spnego_update_post ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
2017-05-11 10:04:02 +03:00
return tevent_req_post ( req , ev ) ;
}
2017-06-14 00:43:01 +03:00
return req ;
2017-05-11 10:04:02 +03:00
}
2017-06-13 17:59:02 +03:00
static NTSTATUS gensec_spnego_update_in ( struct gensec_security * gensec_security ,
2017-06-13 23:41:14 +03:00
const DATA_BLOB in , TALLOC_CTX * mem_ctx ,
DATA_BLOB * full_in )
2017-06-13 17:59:02 +03:00
{
2020-11-11 19:03:29 +03:00
DATA_BLOB consume = data_blob_null ;
2017-07-19 12:02:39 +03:00
struct spnego_state * spnego_state =
talloc_get_type_abort ( gensec_security - > private_data ,
struct spnego_state ) ;
2017-06-13 17:59:02 +03:00
size_t expected ;
bool ok ;
* full_in = data_blob_null ;
2017-06-14 04:29:58 +03:00
switch ( spnego_state - > state_position ) {
case SPNEGO_FALLBACK :
* full_in = in ;
spnego_state - > in_needed = 0 ;
return NT_STATUS_OK ;
case SPNEGO_CLIENT_START :
case SPNEGO_CLIENT_TARG :
case SPNEGO_SERVER_START :
case SPNEGO_SERVER_TARG :
break ;
case SPNEGO_DONE :
default :
return NT_STATUS_INVALID_PARAMETER ;
}
2017-06-13 17:59:02 +03:00
if ( spnego_state - > in_needed = = 0 ) {
size_t size = 0 ;
int ret ;
/*
* try to work out the size of the full
* input token , it might be fragmented
*/
ret = asn1_peek_full_tag ( in , ASN1_APPLICATION ( 0 ) , & size ) ;
if ( ( ret ! = 0 ) & & ( ret ! = EAGAIN ) ) {
ret = asn1_peek_full_tag ( in , ASN1_CONTEXT ( 1 ) , & size ) ;
}
if ( ( ret = = 0 ) | | ( ret = = EAGAIN ) ) {
spnego_state - > in_needed = size ;
} else {
/*
* If it is not an asn1 message
* just call the next layer .
*/
spnego_state - > in_needed = in . length ;
}
}
if ( spnego_state - > in_needed > UINT16_MAX ) {
/*
* limit the incoming message to 0xFFFF
* to avoid DoS attacks .
*/
return NT_STATUS_INVALID_BUFFER_SIZE ;
}
if ( ( spnego_state - > in_needed > 0 ) & & ( in . length = = 0 ) ) {
/*
* If we reach this , we know we got at least
* part of an asn1 message , getting 0 means
* the remote peer wants us to spin .
*/
return NT_STATUS_INVALID_PARAMETER ;
}
2020-11-11 19:03:29 +03:00
consume = in ;
2017-06-13 17:59:02 +03:00
expected = spnego_state - > in_needed - spnego_state - > in_frag . length ;
2020-11-11 19:03:29 +03:00
if ( consume . length > expected ) {
if ( spnego_state - > state_position ! = SPNEGO_SERVER_START ) {
/*
* we got more than expected
*/
return NT_STATUS_INVALID_PARAMETER ;
}
2017-06-13 17:59:02 +03:00
/*
2020-11-11 19:03:29 +03:00
* In SPNEGO_SERVER_START we need to ignore unexpected
* bytes at the end .
2017-06-13 17:59:02 +03:00
*/
2020-11-11 19:03:29 +03:00
consume . length = expected ;
2017-06-13 17:59:02 +03:00
}
2020-11-11 19:03:29 +03:00
if ( consume . length = = spnego_state - > in_needed ) {
2017-06-13 17:59:02 +03:00
/*
2020-11-11 19:03:29 +03:00
* if the consume . length contains the full blob
2017-06-13 17:59:02 +03:00
* we are done .
*
* Note : this implies spnego_state - > in_frag . length = = 0 ,
* but we do not need to check this explicitly
* because we already know that we did not get
* more than expected .
*/
2020-11-11 19:03:29 +03:00
* full_in = consume ;
2017-06-13 23:41:14 +03:00
spnego_state - > in_needed = 0 ;
2017-06-13 17:59:02 +03:00
return NT_STATUS_OK ;
}
ok = data_blob_append ( spnego_state , & spnego_state - > in_frag ,
2020-11-11 19:03:29 +03:00
consume . data , consume . length ) ;
2017-06-13 17:59:02 +03:00
if ( ! ok ) {
return NT_STATUS_NO_MEMORY ;
}
if ( spnego_state - > in_needed > spnego_state - > in_frag . length ) {
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
}
* full_in = spnego_state - > in_frag ;
2017-06-13 23:41:14 +03:00
talloc_steal ( mem_ctx , full_in - > data ) ;
spnego_state - > in_frag = data_blob_null ;
spnego_state - > in_needed = 0 ;
2017-06-13 17:59:02 +03:00
return NT_STATUS_OK ;
}
2017-06-14 00:43:01 +03:00
static void gensec_spnego_update_pre ( struct tevent_req * req )
{
struct gensec_spnego_update_state * state =
tevent_req_data ( req ,
struct gensec_spnego_update_state ) ;
struct spnego_state * spnego_state = state - > spnego ;
2017-06-14 12:01:23 +03:00
const struct spnego_neg_ops * ops = NULL ;
2017-06-14 00:43:01 +03:00
NTSTATUS status ;
2017-06-14 00:43:01 +03:00
state - > sub . needed = false ;
state - > sub . in = data_blob_null ;
state - > sub . status = NT_STATUS_INTERNAL_ERROR ;
state - > sub . out = data_blob_null ;
2017-06-14 00:43:01 +03:00
if ( spnego_state - > state_position = = SPNEGO_FALLBACK ) {
2017-06-14 00:43:01 +03:00
state - > sub . in = state - > full_in ;
state - > full_in = data_blob_null ;
state - > sub . needed = true ;
2017-06-14 00:43:01 +03:00
return ;
}
switch ( spnego_state - > state_position ) {
case SPNEGO_CLIENT_START :
if ( state - > spnego_in = = NULL ) {
/* client to produce negTokenInit */
2017-06-14 12:01:23 +03:00
ops = & gensec_spnego_create_negTokenInit_ops ;
2017-06-14 00:43:01 +03:00
break ;
}
2017-06-14 12:01:23 +03:00
ops = & gensec_spnego_client_negTokenInit_ops ;
2017-06-14 00:43:01 +03:00
break ;
case SPNEGO_CLIENT_TARG :
2017-06-14 12:01:23 +03:00
ops = & gensec_spnego_client_negTokenTarg_ops ;
2017-06-14 00:43:01 +03:00
break ;
case SPNEGO_SERVER_START :
if ( state - > spnego_in = = NULL ) {
/* server to produce negTokenInit */
2017-06-14 12:01:23 +03:00
ops = & gensec_spnego_create_negTokenInit_ops ;
2017-06-14 00:43:01 +03:00
break ;
}
2017-06-14 12:01:23 +03:00
ops = & gensec_spnego_server_negTokenInit_ops ;
2017-06-14 00:43:01 +03:00
break ;
case SPNEGO_SERVER_TARG :
2017-06-14 12:01:23 +03:00
ops = & gensec_spnego_server_negTokenTarg_ops ;
2017-06-14 00:43:01 +03:00
break ;
default :
smb_panic ( __location__ ) ;
return ;
}
2017-06-14 12:01:23 +03:00
state - > n = gensec_spnego_neg_state ( state , ops ) ;
if ( tevent_req_nomem ( state - > n , req ) ) {
return ;
}
status = ops - > start_fn ( state - > gensec , spnego_state , state - > n ,
state - > spnego_in , state , & state - > sub . in ) ;
if ( GENSEC_UPDATE_IS_NTERROR ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
if ( NT_STATUS_IS_OK ( status ) ) {
/*
* Call finish_fn ( ) with an empty
* blob and NT_STATUS_OK .
*/
state - > sub . status = NT_STATUS_OK ;
2019-09-04 16:31:21 +03:00
} else if ( spnego_state - > state_position = = SPNEGO_CLIENT_START & &
spnego_state - > no_optimistic ) {
/*
* Skip optimistic token per conf .
*/
state - > sub . status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
2019-09-04 17:04:12 +03:00
} else if ( spnego_state - > state_position = = SPNEGO_SERVER_START & &
state - > sub . in . length = = 0 & & spnego_state - > no_optimistic ) {
/*
* If we didn ' t like the mechanism for which the client sent us
* an optimistic token , or if he didn ' t send any , don ' t call
* the sub mechanism just yet .
*/
state - > sub . status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
spnego_state - > no_optimistic = false ;
2017-06-14 12:01:23 +03:00
} else {
/*
* MORE_PROCESSING_REQUIRED = >
* we need to call gensec_update_send ( ) .
*/
state - > sub . needed = true ;
}
2017-06-14 00:43:01 +03:00
}
2017-06-14 00:43:01 +03:00
static void gensec_spnego_update_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct gensec_spnego_update_state * state =
tevent_req_data ( req ,
struct gensec_spnego_update_state ) ;
struct spnego_state * spnego_state = state - > spnego ;
state - > sub . status = gensec_update_recv ( subreq , state , & state - > sub . out ) ;
TALLOC_FREE ( subreq ) ;
if ( NT_STATUS_IS_OK ( state - > sub . status ) ) {
spnego_state - > sub_sec_ready = true ;
}
gensec_spnego_update_post ( req ) ;
}
2017-06-14 00:43:01 +03:00
static void gensec_spnego_update_post ( struct tevent_req * req )
{
struct gensec_spnego_update_state * state =
tevent_req_data ( req ,
struct gensec_spnego_update_state ) ;
struct spnego_state * spnego_state = state - > spnego ;
2017-06-14 12:01:23 +03:00
const struct spnego_neg_ops * ops = NULL ;
2017-06-14 00:43:01 +03:00
NTSTATUS status ;
2017-06-14 00:43:01 +03:00
state - > sub . in = data_blob_null ;
state - > sub . needed = false ;
2017-06-14 00:43:01 +03:00
if ( spnego_state - > state_position = = SPNEGO_FALLBACK ) {
2017-06-14 00:43:01 +03:00
status = state - > sub . status ;
spnego_state - > out_frag = state - > sub . out ;
talloc_steal ( spnego_state , spnego_state - > out_frag . data ) ;
state - > sub . out = data_blob_null ;
2017-06-14 00:43:01 +03:00
goto respond ;
}
2017-06-14 12:01:23 +03:00
ops = state - > n - > ops ;
if ( GENSEC_UPDATE_IS_NTERROR ( state - > sub . status ) ) {
/*
* gensec_update_recv ( ) returned an error ,
* let ' s see if the step_fn ( ) want to
* handle it and negotiate something else .
*/
status = ops - > step_fn ( state - > gensec ,
spnego_state ,
state - > n ,
state - > spnego_in ,
state - > sub . status ,
state ,
& state - > sub . in ) ;
if ( GENSEC_UPDATE_IS_NTERROR ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
state - > sub . out = data_blob_null ;
state - > sub . status = NT_STATUS_INTERNAL_ERROR ;
if ( NT_STATUS_IS_OK ( status ) ) {
/*
* Call finish_fn ( ) with an empty
* blob and NT_STATUS_OK .
*/
state - > sub . status = NT_STATUS_OK ;
} else {
/*
* MORE_PROCESSING_REQUIRED . . .
*/
state - > sub . needed = true ;
}
}
if ( state - > sub . needed ) {
struct tevent_req * subreq = NULL ;
/*
* We may need one more roundtrip . . .
*/
subreq = gensec_update_send ( state , state - > ev ,
spnego_state - > sub_sec_security ,
state - > sub . in ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
gensec_spnego_update_done ,
req ) ;
state - > sub . needed = false ;
return ;
}
status = ops - > finish_fn ( state - > gensec ,
spnego_state ,
state - > n ,
state - > spnego_in ,
state - > sub . status ,
state - > sub . out ,
spnego_state ,
& spnego_state - > out_frag ) ;
TALLOC_FREE ( state - > n ) ;
if ( GENSEC_UPDATE_IS_NTERROR ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
2017-06-14 00:43:01 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
bool reset_full = true ;
reset_full = ! spnego_state - > done_mic_check ;
status = gensec_may_reset_crypto ( spnego_state - > sub_sec_security ,
reset_full ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
}
respond :
spnego_state - > out_status = status ;
status = gensec_spnego_update_out ( state - > gensec ,
state , & state - > out ) ;
if ( GENSEC_UPDATE_IS_NTERROR ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
state - > status = status ;
tevent_req_done ( req ) ;
return ;
}
2017-06-13 23:43:59 +03:00
static NTSTATUS gensec_spnego_update_out ( struct gensec_security * gensec_security ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * _out )
{
2017-07-19 12:05:32 +03:00
struct spnego_state * spnego_state =
talloc_get_type_abort ( gensec_security - > private_data ,
struct spnego_state ) ;
2017-06-13 23:43:59 +03:00
DATA_BLOB out = data_blob_null ;
bool ok ;
* _out = data_blob_null ;
if ( spnego_state - > out_frag . length < = spnego_state - > out_max_length ) {
/*
* Fast path , we can deliver everything
*/
* _out = spnego_state - > out_frag ;
if ( spnego_state - > out_frag . length > 0 ) {
talloc_steal ( out_mem_ctx , _out - > data ) ;
spnego_state - > out_frag = data_blob_null ;
}
if ( ! NT_STATUS_IS_OK ( spnego_state - > out_status ) ) {
return spnego_state - > out_status ;
}
/*
* We ' re completely done , further updates are not allowed .
*/
spnego_state - > state_position = SPNEGO_DONE ;
return gensec_child_ready ( gensec_security ,
spnego_state - > sub_sec_security ) ;
}
out = spnego_state - > out_frag ;
/*
* copy the remaining bytes
*/
spnego_state - > out_frag = data_blob_talloc ( spnego_state ,
out . data + spnego_state - > out_max_length ,
out . length - spnego_state - > out_max_length ) ;
if ( spnego_state - > out_frag . data = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/*
* truncate the buffer
*/
ok = data_blob_realloc ( spnego_state , & out ,
spnego_state - > out_max_length ) ;
if ( ! ok ) {
return NT_STATUS_NO_MEMORY ;
}
talloc_steal ( out_mem_ctx , out . data ) ;
* _out = out ;
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
}
2017-05-11 10:04:02 +03:00
static NTSTATUS gensec_spnego_update_recv ( struct tevent_req * req ,
TALLOC_CTX * out_mem_ctx ,
DATA_BLOB * out )
{
struct gensec_spnego_update_state * state =
tevent_req_data ( req ,
struct gensec_spnego_update_state ) ;
NTSTATUS status ;
* out = data_blob_null ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
* out = state - > out ;
talloc_steal ( out_mem_ctx , state - > out . data ) ;
status = state - > status ;
tevent_req_received ( req ) ;
return status ;
}
2005-05-16 03:42:11 +04:00
static const char * gensec_spnego_oids [ ] = {
GENSEC_OID_SPNEGO ,
NULL
} ;
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
static const struct gensec_security_ops gensec_spnego_security_ops = {
r17197: This patch moves the encryption of bulk data on SASL negotiated security
contexts from the application layer into the socket layer.
This improves a number of correctness aspects, as we now allow LDAP
packets to cross multiple SASL packets. It should also make it much
easier to write async LDAP tests from windows clients, as they use SASL
by default. It is also vital to allowing OpenLDAP clients to use GSSAPI
against Samba4, as it negotiates a rather small SASL buffer size.
This patch mirrors the earlier work done to move TLS into the socket
layer.
Unusual in this pstch is the extra read callback argument I take. As
SASL is a layer on top of a socket, it is entirely possible for the
SASL layer to drain a socket dry, but for the caller not to have read
all the decrypted data. This would leave the system without an event
to restart the read (as the socket is dry).
As such, I re-invoke the read handler from a timed callback, which
should trigger on the next running of the event loop. I believe that
the TLS code does require a similar callback.
In trying to understand why this is required, imagine a SASL-encrypted
LDAP packet in the following formation:
+-----------------+---------------------+
| SASL Packet #1 | SASL Packet #2 |
----------------------------------------+
| LDAP Packet #1 | LDAP Packet #2 |
----------------------------------------+
In the old code, this was illegal, but it is perfectly standard
SASL-encrypted LDAP. Without the callback, we would read and process
the first LDAP packet, and the SASL code would have read the second SASL
packet (to decrypt enough data for the LDAP packet), and no data would
remain on the socket.
Without data on the socket, read events stop. That is why I add timed
events, until the SASL buffer is drained.
Another approach would be to add a hack to the event system, to have it
pretend there remained data to read off the network (but that is ugly).
In improving the code, to handle more real-world cases, I've been able
to remove almost all the special-cases in the testnonblock code. The
only special case is that we must use a deterministic partial packet
when calling send, rather than a random length. (1 + n/2). This is
needed because of the way the SASL and TLS code works, and the 'resend
on failure' requirements.
Andrew Bartlett
(This used to be commit 5d7c9c12cb2b39673172a357092b80cd814850b0)
2006-07-23 06:50:08 +04:00
. name = " spnego " ,
. sasl_name = " GSS-SPNEGO " ,
. auth_type = DCERPC_AUTH_TYPE_SPNEGO ,
. oid = gensec_spnego_oids ,
. client_start = gensec_spnego_client_start ,
. server_start = gensec_spnego_server_start ,
2017-05-11 10:04:02 +03:00
. update_send = gensec_spnego_update_send ,
. update_recv = gensec_spnego_update_recv ,
2016-12-30 20:05:17 +03:00
. seal_packet = gensec_child_seal_packet ,
. sign_packet = gensec_child_sign_packet ,
. sig_size = gensec_child_sig_size ,
. max_wrapped_size = gensec_child_max_wrapped_size ,
. max_input_size = gensec_child_max_input_size ,
. check_packet = gensec_child_check_packet ,
. unseal_packet = gensec_child_unseal_packet ,
. wrap = gensec_child_wrap ,
. unwrap = gensec_child_unwrap ,
. session_key = gensec_child_session_key ,
. session_info = gensec_child_session_info ,
. want_feature = gensec_child_want_feature ,
. have_feature = gensec_child_have_feature ,
. expire_time = gensec_child_expire_time ,
. final_auth_type = gensec_child_final_auth_type ,
2007-10-07 02:16:19 +04:00
. enabled = true ,
2017-07-21 00:28:51 +03:00
. priority = GENSEC_SPNEGO ,
. glue = true ,
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
} ;
2017-04-20 22:24:43 +03:00
_PUBLIC_ NTSTATUS gensec_spnego_init ( TALLOC_CTX * ctx )
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
{
NTSTATUS ret ;
2017-05-12 01:56:29 +03:00
ret = gensec_register ( ctx , & gensec_spnego_security_ops ) ;
r1294: A nice, large, commit...
This implements gensec for Samba's server side, and brings gensec up
to the standards of a full subsystem.
This means that use of the subsystem is by gensec_* functions, not
function pointers in structures (this is internal). This causes
changes in all the existing gensec users.
Our RPC server no longer contains it's own generalised security
scheme, and now calls gensec directly.
Gensec has also taken over the role of auth/auth_ntlmssp.c
An important part of gensec, is the output of the 'session_info'
struct. This is now reference counted, so that we can correctly free
it when a pipe is closed, no matter if it was inherited, or created by
per-pipe authentication.
The schannel code is reworked, to be in the same file for client and
server.
ntlm_auth is reworked to use gensec.
The major problem with this code is the way it relies on subsystem
auto-initialisation. The primary reason for this commit now.is to
allow these problems to be looked at, and fixed.
There are problems with the new code:
- I've tested it with smbtorture, but currently don't have VMware and
valgrind working (this I'll fix soon).
- The SPNEGO code is client-only at this point.
- We still do not do kerberos.
Andrew Bartlett
(This used to be commit 07fd885fd488fd1051eacc905a2d4962f8a018ec)
2004-06-29 13:40:10 +04:00
if ( ! NT_STATUS_IS_OK ( ret ) ) {
DEBUG ( 0 , ( " Failed to register '%s' gensec backend! \n " ,
gensec_spnego_security_ops . name ) ) ;
return ret ;
}
return ret ;
}