2011-05-05 18:12:07 +02:00
/*
Unix SMB / CIFS implementation .
smb2 lib
Copyright ( C ) Volker Lendecke 2011
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/>.
*/
# include "includes.h"
# include "client.h"
# include "async_smb.h"
# include "smb2cli_base.h"
# include "smb2cli.h"
# include "libsmb/proto.h"
# include "lib/util/tevent_ntstatus.h"
# include "../libcli/auth/spnego.h"
2011-07-25 16:04:38 +10:00
# include "../auth/ntlmssp/ntlmssp.h"
2011-05-05 18:12:07 +02:00
2011-09-18 20:23:22 +02:00
struct smb2cli_session_setup_state {
2011-05-05 18:12:07 +02:00
uint8_t fixed [ 24 ] ;
2011-09-05 18:22:57 +02:00
uint8_t dyn_pad [ 1 ] ;
2011-05-05 18:12:07 +02:00
uint64_t uid ;
DATA_BLOB out ;
} ;
2011-09-18 20:23:22 +02:00
static void smb2cli_session_setup_done ( struct tevent_req * subreq ) ;
2011-05-05 18:12:07 +02:00
2011-09-18 20:23:22 +02:00
static struct tevent_req * smb2cli_session_setup_send ( TALLOC_CTX * mem_ctx ,
2011-05-05 18:12:07 +02:00
struct tevent_context * ev ,
struct cli_state * cli ,
DATA_BLOB * blob )
{
struct tevent_req * req , * subreq ;
2011-09-18 20:23:22 +02:00
struct smb2cli_session_setup_state * state ;
2011-05-05 18:12:07 +02:00
uint8_t * buf ;
2011-09-05 18:22:57 +02:00
uint8_t * dyn ;
size_t dyn_len ;
2011-05-05 18:12:07 +02:00
req = tevent_req_create ( mem_ctx , & state ,
2011-09-18 20:23:22 +02:00
struct smb2cli_session_setup_state ) ;
2011-05-05 18:12:07 +02:00
if ( req = = NULL ) {
return NULL ;
}
buf = state - > fixed ;
SSVAL ( buf , 0 , 25 ) ;
SCVAL ( buf , 2 , 0 ) ; /* VcNumber */
SCVAL ( buf , 3 , 0 ) ; /* SecurityMode */
SIVAL ( buf , 4 , 0 ) ; /* Capabilities */
SIVAL ( buf , 8 , 0 ) ; /* Channel */
SSVAL ( buf , 12 , SMB2_HDR_BODY + 24 ) ; /* SecurityBufferOffset */
SSVAL ( buf , 14 , blob - > length ) ;
SBVAL ( buf , 16 , 0 ) ; /* PreviousSessionId */
2011-09-05 18:22:57 +02:00
if ( blob - > length > 0 ) {
dyn = blob - > data ;
dyn_len = blob - > length ;
} else {
dyn = state - > dyn_pad ; ;
dyn_len = sizeof ( state - > dyn_pad ) ;
}
2011-08-12 17:26:13 +02:00
subreq = smb2cli_req_send ( state , ev , cli , SMB2_OP_SESSSETUP ,
0 , 0 , /* flags */
2011-09-17 19:56:50 +02:00
cli - > timeout ,
2011-08-12 17:26:13 +02:00
cli - > smb2 . pid ,
2011-08-31 00:32:48 +02:00
0 , /* tid */
cli - > smb2 . uid ,
2011-05-05 18:12:07 +02:00
state - > fixed , sizeof ( state - > fixed ) ,
2011-09-05 18:22:57 +02:00
dyn , dyn_len ) ;
2011-05-05 18:12:07 +02:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-09-18 20:23:22 +02:00
tevent_req_set_callback ( subreq , smb2cli_session_setup_done , req ) ;
2011-05-05 18:12:07 +02:00
return req ;
}
2011-09-18 20:23:22 +02:00
static void smb2cli_session_setup_done ( struct tevent_req * subreq )
2011-05-05 18:12:07 +02:00
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
2011-09-18 20:23:22 +02:00
struct smb2cli_session_setup_state * state =
2011-05-05 18:12:07 +02:00
tevent_req_data ( req ,
2011-09-18 20:23:22 +02:00
struct smb2cli_session_setup_state ) ;
2011-05-05 18:12:07 +02:00
NTSTATUS status ;
struct iovec * iov ;
uint16_t offset , length ;
2011-08-31 00:40:06 +02:00
static const struct smb2cli_req_expected_response expected [ ] = {
{
. status = NT_STATUS_MORE_PROCESSING_REQUIRED ,
. body_size = 0x09
} ,
{
. status = NT_STATUS_OK ,
. body_size = 0x09
}
} ;
status = smb2cli_req_recv ( subreq , talloc_tos ( ) , & iov ,
expected , ARRAY_SIZE ( expected ) ) ;
2011-05-05 18:12:07 +02:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
TALLOC_FREE ( subreq ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
offset = SVAL ( iov [ 1 ] . iov_base , 4 ) ;
length = SVAL ( iov [ 1 ] . iov_base , 6 ) ;
if ( ( offset ! = SMB2_HDR_BODY + 8 ) | | ( length > iov [ 2 ] . iov_len ) ) {
TALLOC_FREE ( subreq ) ;
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
state - > uid = BVAL ( iov [ 0 ] . iov_base , SMB2_HDR_SESSION_ID ) ;
state - > out . data = ( uint8_t * ) iov [ 2 ] . iov_base ;
state - > out . length = length ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
tevent_req_done ( req ) ;
}
2011-09-18 20:23:22 +02:00
static NTSTATUS smb2cli_session_setup_recv ( struct tevent_req * req ,
2011-05-05 18:12:07 +02:00
uint64_t * uid , DATA_BLOB * out )
{
2011-09-18 20:23:22 +02:00
struct smb2cli_session_setup_state * state =
2011-05-05 18:12:07 +02:00
tevent_req_data ( req ,
2011-09-18 20:23:22 +02:00
struct smb2cli_session_setup_state ) ;
2011-05-05 18:12:07 +02:00
NTSTATUS status = NT_STATUS_OK ;
if ( tevent_req_is_nterror ( req , & status )
& & ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
return status ;
}
* uid = state - > uid ;
* out = state - > out ;
return status ;
}
2011-09-18 20:09:48 +02:00
struct smb2cli_sesssetup_ntlmssp_state {
2011-05-05 18:12:07 +02:00
struct tevent_context * ev ;
struct cli_state * cli ;
struct ntlmssp_state * ntlmssp ;
struct iovec iov [ 2 ] ;
uint8_t fixed [ 24 ] ;
DATA_BLOB msg ;
int turn ;
} ;
2011-09-18 20:09:48 +02:00
static void smb2cli_sesssetup_ntlmssp_done ( struct tevent_req * subreq ) ;
2011-05-05 18:12:07 +02:00
2011-09-18 20:09:48 +02:00
struct tevent_req * smb2cli_sesssetup_ntlmssp_send ( TALLOC_CTX * mem_ctx ,
2011-05-05 18:12:07 +02:00
struct tevent_context * ev ,
struct cli_state * cli ,
const char * user ,
const char * domain ,
const char * pass )
{
struct tevent_req * req , * subreq ;
2011-09-18 20:09:48 +02:00
struct smb2cli_sesssetup_ntlmssp_state * state ;
2011-05-05 18:12:07 +02:00
NTSTATUS status ;
DATA_BLOB blob_out ;
const char * OIDs_ntlm [ ] = { OID_NTLMSSP , NULL } ;
req = tevent_req_create ( mem_ctx , & state ,
2011-09-18 20:09:48 +02:00
struct smb2cli_sesssetup_ntlmssp_state ) ;
2011-05-05 18:12:07 +02:00
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > cli = cli ;
status = ntlmssp_client_start ( state ,
lp_netbios_name ( ) ,
lp_workgroup ( ) ,
lp_client_ntlmv2_auth ( ) ,
& state - > ntlmssp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto post_status ;
}
2011-09-03 09:53:13 +02:00
ntlmssp_want_feature ( state - > ntlmssp ,
NTLMSSP_FEATURE_SESSION_KEY ) ;
2011-05-05 18:12:07 +02:00
status = ntlmssp_set_username ( state - > ntlmssp , user ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto post_status ;
}
status = ntlmssp_set_domain ( state - > ntlmssp , domain ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto post_status ;
}
status = ntlmssp_set_password ( state - > ntlmssp , pass ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto post_status ;
}
status = ntlmssp_update ( state - > ntlmssp , data_blob_null , & blob_out ) ;
if ( ! NT_STATUS_IS_OK ( status )
& & ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
goto post_status ;
}
blob_out = spnego_gen_negTokenInit ( state , OIDs_ntlm , & blob_out , NULL ) ;
state - > turn = 1 ;
2011-09-18 20:23:22 +02:00
subreq = smb2cli_session_setup_send (
2011-05-05 18:12:07 +02:00
state , state - > ev , state - > cli , & blob_out ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-09-18 20:09:48 +02:00
tevent_req_set_callback ( subreq , smb2cli_sesssetup_ntlmssp_done , req ) ;
2011-05-05 18:12:07 +02:00
return req ;
post_status :
tevent_req_nterror ( req , status ) ;
return tevent_req_post ( req , ev ) ;
}
2011-09-18 20:09:48 +02:00
static void smb2cli_sesssetup_ntlmssp_done ( struct tevent_req * subreq )
2011-05-05 18:12:07 +02:00
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
2011-09-18 20:09:48 +02:00
struct smb2cli_sesssetup_ntlmssp_state * state =
2011-05-05 18:12:07 +02:00
tevent_req_data ( req ,
2011-09-18 20:09:48 +02:00
struct smb2cli_sesssetup_ntlmssp_state ) ;
2011-05-05 18:12:07 +02:00
NTSTATUS status ;
uint64_t uid = 0 ;
DATA_BLOB blob , blob_in , blob_out , spnego_blob ;
bool ret ;
2011-09-18 20:23:22 +02:00
status = smb2cli_session_setup_recv ( subreq , & uid , & blob ) ;
2011-05-05 18:12:07 +02:00
if ( ! NT_STATUS_IS_OK ( status )
& & ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
TALLOC_FREE ( subreq ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
if ( NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( subreq ) ;
tevent_req_done ( req ) ;
return ;
}
if ( state - > turn = = 1 ) {
DATA_BLOB tmp_blob = data_blob_null ;
ret = spnego_parse_challenge ( state , blob , & blob_in , & tmp_blob ) ;
data_blob_free ( & tmp_blob ) ;
} else {
ret = spnego_parse_auth_response ( state , blob , status ,
OID_NTLMSSP , & blob_in ) ;
}
TALLOC_FREE ( subreq ) ;
if ( ! ret ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
status = ntlmssp_update ( state - > ntlmssp , blob_in , & blob_out ) ;
data_blob_free ( & blob_in ) ;
state - > turn + = 1 ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
state - > cli - > smb2 . uid = uid ;
spnego_blob = spnego_gen_auth ( state , blob_out ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nomem ( spnego_blob . data , req ) ) {
return ;
}
2011-09-18 20:23:22 +02:00
subreq = smb2cli_session_setup_send (
2011-05-05 18:12:07 +02:00
state , state - > ev , state - > cli , & spnego_blob ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
2011-09-18 20:09:48 +02:00
tevent_req_set_callback ( subreq , smb2cli_sesssetup_ntlmssp_done , req ) ;
2011-05-05 18:12:07 +02:00
}
2011-09-18 20:09:48 +02:00
NTSTATUS smb2cli_sesssetup_ntlmssp_recv ( struct tevent_req * req )
2011-05-05 18:12:07 +02:00
{
return tevent_req_simple_recv_ntstatus ( req ) ;
}
2011-09-18 20:09:48 +02:00
NTSTATUS smb2cli_sesssetup_ntlmssp ( struct cli_state * cli , const char * user ,
2011-05-05 18:12:07 +02:00
const char * domain , const char * pass )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct event_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
if ( cli_has_async_calls ( cli ) ) {
/*
* Can ' t use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
}
ev = event_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
2011-09-18 20:09:48 +02:00
req = smb2cli_sesssetup_ntlmssp_send ( frame , ev , cli , user , domain , pass ) ;
2011-05-05 18:12:07 +02:00
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
2011-09-18 20:09:48 +02:00
status = smb2cli_sesssetup_ntlmssp_recv ( req ) ;
2011-05-05 18:12:07 +02:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
struct smb2cli_logoff_state {
uint8_t fixed [ 4 ] ;
} ;
static void smb2cli_logoff_done ( struct tevent_req * subreq ) ;
struct tevent_req * smb2cli_logoff_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct cli_state * cli )
{
struct tevent_req * req , * subreq ;
struct smb2cli_logoff_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct smb2cli_logoff_state ) ;
if ( req = = NULL ) {
return NULL ;
}
SSVAL ( state - > fixed , 0 , 4 ) ;
2011-08-12 17:26:13 +02:00
subreq = smb2cli_req_send ( state , ev , cli , SMB2_OP_LOGOFF ,
0 , 0 , /* flags */
2011-09-17 19:56:50 +02:00
cli - > timeout ,
2011-08-12 17:26:13 +02:00
cli - > smb2 . pid ,
0 , /* tid */
cli - > smb2 . uid ,
2011-05-05 18:12:07 +02:00
state - > fixed , sizeof ( state - > fixed ) ,
NULL , 0 ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smb2cli_logoff_done , req ) ;
return req ;
}
static void smb2cli_logoff_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
NTSTATUS status ;
struct iovec * iov ;
2011-08-31 00:40:06 +02:00
static const struct smb2cli_req_expected_response expected [ ] = {
{
. status = NT_STATUS_OK ,
. body_size = 0x04
}
} ;
2011-05-05 18:12:07 +02:00
2011-08-31 00:40:06 +02:00
status = smb2cli_req_recv ( subreq , talloc_tos ( ) , & iov ,
expected , ARRAY_SIZE ( expected ) ) ;
2011-05-05 18:12:07 +02:00
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
tevent_req_done ( req ) ;
}
NTSTATUS smb2cli_logoff_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
}
NTSTATUS smb2cli_logoff ( struct cli_state * cli )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct event_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
if ( cli_has_async_calls ( cli ) ) {
/*
* Can ' t use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
}
ev = event_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = smb2cli_logoff_send ( frame , ev , cli ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = smb2cli_logoff_recv ( req ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}