2009-03-12 20:10:19 +03:00
/*
Unix SMB / CIFS implementation .
Copyright ( C ) Stefan Metzmacher 2009
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 "system/network.h"
# include "../util/tevent_unix.h"
# include "../lib/tsocket/tsocket.h"
# include "../lib/tsocket/tsocket_internal.h"
# include "../librpc/gen_ndr/ndr_named_pipe_auth.h"
# include "../libcli/named_pipe_auth/npa_tstream.h"
2011-07-06 07:06:14 +04:00
# include "../libcli/smb/smb_constants.h"
2009-03-12 20:10:19 +03:00
static const struct tstream_context_ops tstream_npa_ops ;
struct tstream_npa {
struct tstream_context * unix_stream ;
uint16_t file_type ;
struct iovec pending ;
} ;
struct tstream_npa_connect_state {
struct {
struct tevent_context * ev ;
} caller ;
const char * unix_path ;
struct tsocket_address * unix_laddr ;
struct tsocket_address * unix_raddr ;
struct tstream_context * unix_stream ;
struct named_pipe_auth_req auth_req ;
DATA_BLOB auth_req_blob ;
struct iovec auth_req_iov ;
struct named_pipe_auth_rep auth_rep ;
DATA_BLOB auth_rep_blob ;
} ;
static void tstream_npa_connect_unix_done ( struct tevent_req * subreq ) ;
struct tevent_req * tstream_npa_connect_send ( TALLOC_CTX * mem_ctx ,
2011-02-09 06:22:16 +03:00
struct tevent_context * ev ,
const char * directory ,
const char * npipe ,
const struct tsocket_address * client ,
const char * client_name_in ,
const struct tsocket_address * server ,
const char * server_name ,
const struct auth_session_info_transport * session_info )
2009-03-12 20:10:19 +03:00
{
struct tevent_req * req ;
struct tstream_npa_connect_state * state ;
struct tevent_req * subreq ;
int ret ;
enum ndr_err_code ndr_err ;
2010-06-21 18:13:14 +04:00
char * lower_case_npipe ;
2011-02-09 06:22:16 +03:00
struct named_pipe_auth_req_info4 * info4 ;
2009-03-12 20:10:19 +03:00
req = tevent_req_create ( mem_ctx , & state ,
struct tstream_npa_connect_state ) ;
if ( ! req ) {
return NULL ;
}
state - > caller . ev = ev ;
2010-06-21 18:13:14 +04:00
lower_case_npipe = strlower_talloc ( state , npipe ) ;
if ( tevent_req_nomem ( lower_case_npipe , req ) ) {
goto post ;
}
2009-03-12 20:10:19 +03:00
state - > unix_path = talloc_asprintf ( state , " %s/%s " ,
directory ,
2010-06-03 14:59:25 +04:00
lower_case_npipe ) ;
talloc_free ( lower_case_npipe ) ;
2009-03-12 20:10:19 +03:00
if ( tevent_req_nomem ( state - > unix_path , req ) ) {
goto post ;
}
ret = tsocket_address_unix_from_path ( state ,
" " ,
& state - > unix_laddr ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , errno ) ;
goto post ;
}
ret = tsocket_address_unix_from_path ( state ,
state - > unix_path ,
& state - > unix_raddr ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , errno ) ;
goto post ;
}
ZERO_STRUCT ( state - > auth_req ) ;
2011-02-09 03:59:50 +03:00
if ( ! server ) {
tevent_req_error ( req , EINVAL ) ;
goto post ;
}
2009-03-12 20:10:19 +03:00
2011-02-09 06:22:16 +03:00
state - > auth_req . level = 4 ;
info4 = & state - > auth_req . info . info4 ;
2009-03-12 20:10:19 +03:00
2011-02-09 06:22:16 +03:00
info4 - > client_name = client_name_in ;
info4 - > client_addr = tsocket_address_inet_addr_string ( client , state ) ;
if ( ! info4 - > client_addr ) {
2011-02-09 03:59:50 +03:00
/* errno might be EINVAL */
tevent_req_error ( req , errno ) ;
goto post ;
}
2011-02-09 06:22:16 +03:00
info4 - > client_port = tsocket_address_inet_port ( client ) ;
if ( ! info4 - > client_name ) {
info4 - > client_name = info4 - > client_addr ;
2011-02-09 03:59:50 +03:00
}
2009-09-17 20:43:41 +04:00
2011-02-09 06:22:16 +03:00
info4 - > server_addr = tsocket_address_inet_addr_string ( server , state ) ;
if ( ! info4 - > server_addr ) {
2011-02-09 03:59:50 +03:00
/* errno might be EINVAL */
tevent_req_error ( req , errno ) ;
goto post ;
}
2011-02-09 06:22:16 +03:00
info4 - > server_port = tsocket_address_inet_port ( server ) ;
if ( ! info4 - > server_name ) {
info4 - > server_name = info4 - > server_addr ;
2009-03-12 20:10:19 +03:00
}
2011-02-09 06:22:16 +03:00
info4 - > session_info = discard_const_p ( struct auth_session_info_transport , session_info ) ;
2011-02-09 03:59:50 +03:00
2009-03-12 20:10:19 +03:00
if ( DEBUGLVL ( 10 ) ) {
NDR_PRINT_DEBUG ( named_pipe_auth_req , & state - > auth_req ) ;
}
ndr_err = ndr_push_struct_blob ( & state - > auth_req_blob ,
2010-05-09 19:20:01 +04:00
state , & state - > auth_req ,
2009-03-12 20:10:19 +03:00
( ndr_push_flags_fn_t ) ndr_push_named_pipe_auth_req ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
tevent_req_error ( req , EINVAL ) ;
goto post ;
}
2010-09-11 00:22:14 +04:00
state - > auth_req_iov . iov_base = ( char * ) state - > auth_req_blob . data ;
2009-03-12 20:10:19 +03:00
state - > auth_req_iov . iov_len = state - > auth_req_blob . length ;
subreq = tstream_unix_connect_send ( state ,
state - > caller . ev ,
state - > unix_laddr ,
state - > unix_raddr ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
goto post ;
}
tevent_req_set_callback ( subreq , tstream_npa_connect_unix_done , req ) ;
return req ;
post :
tevent_req_post ( req , ev ) ;
return req ;
}
static void tstream_npa_connect_writev_done ( struct tevent_req * subreq ) ;
static void tstream_npa_connect_unix_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_npa_connect_state * state =
tevent_req_data ( req ,
struct tstream_npa_connect_state ) ;
int ret ;
int sys_errno ;
ret = tstream_unix_connect_recv ( subreq , & sys_errno ,
state , & state - > unix_stream ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
subreq = tstream_writev_send ( state ,
state - > caller . ev ,
state - > unix_stream ,
& state - > auth_req_iov , 1 ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , tstream_npa_connect_writev_done , req ) ;
}
static int tstream_npa_connect_next_vector ( struct tstream_context * unix_stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count ) ;
static void tstream_npa_connect_readv_done ( struct tevent_req * subreq ) ;
static void tstream_npa_connect_writev_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_npa_connect_state * state =
tevent_req_data ( req ,
struct tstream_npa_connect_state ) ;
int ret ;
int sys_errno ;
ret = tstream_writev_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
state - > auth_rep_blob = data_blob_const ( NULL , 0 ) ;
subreq = tstream_readv_pdu_send ( state , state - > caller . ev ,
state - > unix_stream ,
tstream_npa_connect_next_vector ,
state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , tstream_npa_connect_readv_done , req ) ;
}
static int tstream_npa_connect_next_vector ( struct tstream_context * unix_stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count )
{
struct tstream_npa_connect_state * state = talloc_get_type_abort ( private_data ,
struct tstream_npa_connect_state ) ;
struct iovec * vector ;
size_t count ;
off_t ofs = 0 ;
if ( state - > auth_rep_blob . length = = 0 ) {
state - > auth_rep_blob = data_blob_talloc ( state , NULL , 4 ) ;
if ( ! state - > auth_rep_blob . data ) {
return - 1 ;
}
} else if ( state - > auth_rep_blob . length = = 4 ) {
uint32_t msg_len ;
ofs = 4 ;
msg_len = RIVAL ( state - > auth_rep_blob . data , 0 ) ;
if ( msg_len > 0x00FFFFFF ) {
errno = EMSGSIZE ;
return - 1 ;
}
if ( msg_len = = 0 ) {
errno = EMSGSIZE ;
return - 1 ;
}
msg_len + = ofs ;
state - > auth_rep_blob . data = talloc_realloc ( state ,
state - > auth_rep_blob . data ,
uint8_t , msg_len ) ;
if ( ! state - > auth_rep_blob . data ) {
return - 1 ;
}
state - > auth_rep_blob . length = msg_len ;
} else {
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
/* we need to get a message header */
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( ! vector ) {
return - 1 ;
}
2010-09-11 00:22:14 +04:00
vector [ 0 ] . iov_base = ( char * ) ( state - > auth_rep_blob . data + ofs ) ;
2009-03-12 20:10:19 +03:00
vector [ 0 ] . iov_len = state - > auth_rep_blob . length - ofs ;
count = 1 ;
* _vector = vector ;
* _count = count ;
return 0 ;
}
static void tstream_npa_connect_readv_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_npa_connect_state * state =
tevent_req_data ( req ,
struct tstream_npa_connect_state ) ;
int ret ;
int sys_errno ;
enum ndr_err_code ndr_err ;
ret = tstream_readv_pdu_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
DEBUG ( 10 , ( " name_pipe_auth_rep(client)[%u] \n " ,
( uint32_t ) state - > auth_rep_blob . length ) ) ;
dump_data ( 11 , state - > auth_rep_blob . data , state - > auth_rep_blob . length ) ;
ndr_err = ndr_pull_struct_blob (
& state - > auth_rep_blob , state ,
2010-05-09 19:20:01 +04:00
& state - > auth_rep ,
2009-03-12 20:10:19 +03:00
( ndr_pull_flags_fn_t ) ndr_pull_named_pipe_auth_rep ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 0 , ( " ndr_pull_named_pipe_auth_rep failed: %s \n " ,
ndr_map_error2string ( ndr_err ) ) ) ;
tevent_req_error ( req , EIO ) ;
return ;
}
if ( DEBUGLVL ( 10 ) ) {
NDR_PRINT_DEBUG ( named_pipe_auth_rep , & state - > auth_rep ) ;
}
if ( state - > auth_rep . length < 16 ) {
DEBUG ( 0 , ( " req invalid length: %u < 16 \n " ,
state - > auth_rep . length ) ) ;
tevent_req_error ( req , EIO ) ;
return ;
}
if ( strcmp ( NAMED_PIPE_AUTH_MAGIC , state - > auth_rep . magic ) ! = 0 ) {
DEBUG ( 0 , ( " req invalid magic: %s != %s \n " ,
state - > auth_rep . magic , NAMED_PIPE_AUTH_MAGIC ) ) ;
tevent_req_error ( req , EIO ) ;
return ;
}
if ( ! NT_STATUS_IS_OK ( state - > auth_rep . status ) ) {
DEBUG ( 0 , ( " req failed: %s \n " ,
nt_errstr ( state - > auth_rep . status ) ) ) ;
tevent_req_error ( req , EACCES ) ;
return ;
}
if ( state - > auth_rep . level ! = state - > auth_req . level ) {
DEBUG ( 0 , ( " req invalid level: %u != %u \n " ,
state - > auth_rep . level , state - > auth_req . level ) ) ;
tevent_req_error ( req , EIO ) ;
return ;
}
tevent_req_done ( req ) ;
}
int _tstream_npa_connect_recv ( struct tevent_req * req ,
int * perrno ,
TALLOC_CTX * mem_ctx ,
struct tstream_context * * _stream ,
uint16_t * _file_type ,
uint16_t * _device_state ,
uint64_t * _allocation_size ,
const char * location )
{
struct tstream_npa_connect_state * state =
tevent_req_data ( req ,
struct tstream_npa_connect_state ) ;
struct tstream_context * stream ;
struct tstream_npa * npas ;
uint16_t device_state = 0 ;
uint64_t allocation_size = 0 ;
if ( tevent_req_is_unix_error ( req , perrno ) ) {
tevent_req_received ( req ) ;
return - 1 ;
}
stream = tstream_context_create ( mem_ctx ,
& tstream_npa_ops ,
& npas ,
struct tstream_npa ,
location ) ;
if ( ! stream ) {
2010-06-21 18:20:25 +04:00
* perrno = ENOMEM ;
tevent_req_received ( req ) ;
2009-03-12 20:10:19 +03:00
return - 1 ;
}
ZERO_STRUCTP ( npas ) ;
npas - > unix_stream = talloc_move ( stream , & state - > unix_stream ) ;
switch ( state - > auth_rep . level ) {
2011-02-09 06:22:16 +03:00
case 4 :
npas - > file_type = state - > auth_rep . info . info4 . file_type ;
device_state = state - > auth_rep . info . info4 . device_state ;
allocation_size = state - > auth_rep . info . info4 . allocation_size ;
2009-09-17 20:43:41 +04:00
break ;
2009-03-12 20:10:19 +03:00
}
* _stream = stream ;
* _file_type = npas - > file_type ;
* _device_state = device_state ;
* _allocation_size = allocation_size ;
tevent_req_received ( req ) ;
return 0 ;
}
static ssize_t tstream_npa_pending_bytes ( struct tstream_context * stream )
{
struct tstream_npa * npas = tstream_context_data ( stream ,
struct tstream_npa ) ;
ssize_t ret ;
if ( ! npas - > unix_stream ) {
errno = ENOTCONN ;
return - 1 ;
}
switch ( npas - > file_type ) {
case FILE_TYPE_BYTE_MODE_PIPE :
ret = tstream_pending_bytes ( npas - > unix_stream ) ;
break ;
case FILE_TYPE_MESSAGE_MODE_PIPE :
ret = npas - > pending . iov_len ;
break ;
2009-05-10 09:42:55 +04:00
default :
ret = - 1 ;
2009-03-12 20:10:19 +03:00
}
return ret ;
}
struct tstream_npa_readv_state {
struct tstream_context * stream ;
struct iovec * vector ;
size_t count ;
/* the header for message mode */
uint8_t hdr [ 2 ] ;
bool wait_for_hdr ;
int ret ;
} ;
static void tstream_npa_readv_byte_mode_handler ( struct tevent_req * subreq ) ;
static int tstream_npa_readv_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count ) ;
static void tstream_npa_readv_msg_mode_handler ( struct tevent_req * subreq ) ;
static struct tevent_req * tstream_npa_readv_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tstream_context * stream ,
struct iovec * vector ,
size_t count )
{
struct tevent_req * req ;
struct tstream_npa_readv_state * state ;
struct tstream_npa * npas = tstream_context_data ( stream , struct tstream_npa ) ;
struct tevent_req * subreq ;
off_t ofs ;
size_t left ;
uint8_t * pbase ;
req = tevent_req_create ( mem_ctx , & state ,
struct tstream_npa_readv_state ) ;
if ( ! req ) {
return NULL ;
}
state - > stream = stream ;
state - > ret = 0 ;
if ( ! npas - > unix_stream ) {
tevent_req_error ( req , ENOTCONN ) ;
goto post ;
}
switch ( npas - > file_type ) {
case FILE_TYPE_BYTE_MODE_PIPE :
state - > vector = vector ;
state - > count = count ;
subreq = tstream_readv_send ( state ,
ev ,
npas - > unix_stream ,
state - > vector ,
state - > count ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
goto post ;
}
tevent_req_set_callback ( subreq ,
tstream_npa_readv_byte_mode_handler ,
req ) ;
return req ;
case FILE_TYPE_MESSAGE_MODE_PIPE :
/*
* we make a copy of the vector and prepend a header
* with the length
*/
state - > vector = talloc_array ( state , struct iovec , count ) ;
if ( tevent_req_nomem ( state - > vector , req ) ) {
goto post ;
}
memcpy ( state - > vector , vector , sizeof ( struct iovec ) * count ) ;
state - > count = count ;
/*
* copy the pending buffer first
*/
ofs = 0 ;
left = npas - > pending . iov_len ;
pbase = ( uint8_t * ) npas - > pending . iov_base ;
while ( left > 0 & & state - > count > 0 ) {
uint8_t * base ;
base = ( uint8_t * ) state - > vector [ 0 ] . iov_base ;
if ( left < state - > vector [ 0 ] . iov_len ) {
memcpy ( base , pbase + ofs , left ) ;
base + = left ;
2010-09-11 00:22:14 +04:00
state - > vector [ 0 ] . iov_base = ( char * ) base ;
2009-03-12 20:10:19 +03:00
state - > vector [ 0 ] . iov_len - = left ;
ofs + = left ;
left = 0 ;
TALLOC_FREE ( pbase ) ;
ZERO_STRUCT ( npas - > pending ) ;
break ;
}
memcpy ( base , pbase + ofs , state - > vector [ 0 ] . iov_len ) ;
ofs + = state - > vector [ 0 ] . iov_len ;
left - = state - > vector [ 0 ] . iov_len ;
state - > vector + = 1 ;
state - > count - = 1 ;
if ( left = = 0 ) {
TALLOC_FREE ( pbase ) ;
ZERO_STRUCT ( npas - > pending ) ;
break ;
}
}
if ( left > 0 ) {
memmove ( pbase , pbase + ofs , left ) ;
2010-09-11 00:22:14 +04:00
npas - > pending . iov_base = ( char * ) pbase ;
2009-03-12 20:10:19 +03:00
npas - > pending . iov_len = left ;
/*
* this cannot fail and even if it
* fails we can handle it
*/
pbase = talloc_realloc ( npas , pbase , uint8_t , left ) ;
if ( pbase ) {
2010-09-11 00:22:14 +04:00
npas - > pending . iov_base = ( char * ) pbase ;
2009-03-12 20:10:19 +03:00
}
pbase = NULL ;
}
state - > ret + = ofs ;
if ( state - > count = = 0 ) {
tevent_req_done ( req ) ;
goto post ;
}
ZERO_STRUCT ( state - > hdr ) ;
state - > wait_for_hdr = false ;
subreq = tstream_readv_pdu_send ( state ,
ev ,
npas - > unix_stream ,
tstream_npa_readv_next_vector ,
state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
goto post ;
}
tevent_req_set_callback ( subreq ,
tstream_npa_readv_msg_mode_handler ,
req ) ;
return req ;
}
/* this can't happen */
tevent_req_error ( req , EINVAL ) ;
goto post ;
post :
tevent_req_post ( req , ev ) ;
return req ;
}
static void tstream_npa_readv_byte_mode_handler ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_npa_readv_state * state = tevent_req_data ( req ,
struct tstream_npa_readv_state ) ;
int ret ;
int sys_errno ;
ret = tstream_readv_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
state - > ret = ret ;
tevent_req_done ( req ) ;
}
static int tstream_npa_readv_next_vector ( struct tstream_context * unix_stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count )
{
struct tstream_npa_readv_state * state = talloc_get_type_abort ( private_data ,
struct tstream_npa_readv_state ) ;
struct tstream_npa * npas = tstream_context_data ( state - > stream ,
struct tstream_npa ) ;
struct iovec * vector ;
size_t count ;
uint16_t msg_len ;
size_t left ;
if ( state - > count = = 0 ) {
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
if ( ! state - > wait_for_hdr ) {
/* we need to get a message header */
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( ! vector ) {
return - 1 ;
}
ZERO_STRUCT ( state - > hdr ) ;
2010-09-11 00:22:14 +04:00
vector [ 0 ] . iov_base = ( char * ) state - > hdr ;
2009-03-12 20:10:19 +03:00
vector [ 0 ] . iov_len = sizeof ( state - > hdr ) ;
count = 1 ;
state - > wait_for_hdr = true ;
* _vector = vector ;
* _count = count ;
return 0 ;
}
/* and now fill the callers buffers and maybe the pending buffer */
state - > wait_for_hdr = false ;
msg_len = SVAL ( state - > hdr , 0 ) ;
if ( msg_len = = 0 ) {
errno = EIO ;
return - 1 ;
}
state - > wait_for_hdr = false ;
/* +1 because we may need to fill the pending buffer */
vector = talloc_array ( mem_ctx , struct iovec , state - > count + 1 ) ;
if ( ! vector ) {
return - 1 ;
}
count = 0 ;
left = msg_len ;
while ( left > 0 & & state - > count > 0 ) {
if ( left < state - > vector [ 0 ] . iov_len ) {
uint8_t * base ;
base = ( uint8_t * ) state - > vector [ 0 ] . iov_base ;
2010-09-11 00:22:14 +04:00
vector [ count ] . iov_base = ( char * ) base ;
2009-03-12 20:10:19 +03:00
vector [ count ] . iov_len = left ;
count + + ;
base + = left ;
2010-09-11 00:22:14 +04:00
state - > vector [ 0 ] . iov_base = ( char * ) base ;
2009-03-12 20:10:19 +03:00
state - > vector [ 0 ] . iov_len - = left ;
break ;
}
vector [ count ] = state - > vector [ 0 ] ;
count + + ;
left - = state - > vector [ 0 ] . iov_len ;
state - > vector + = 1 ;
state - > count - = 1 ;
}
if ( left > 0 ) {
/*
2010-04-28 12:43:19 +04:00
* if the message is longer than the buffers the caller
2009-03-12 20:10:19 +03:00
* requested , we need to consume the rest of the message
* into the pending buffer , where the next readv can
* be served from .
*/
2010-09-11 00:22:14 +04:00
npas - > pending . iov_base = talloc_array ( npas , char , left ) ;
2009-03-12 20:10:19 +03:00
if ( ! npas - > pending . iov_base ) {
return - 1 ;
}
npas - > pending . iov_len = left ;
vector [ count ] = npas - > pending ;
count + + ;
}
state - > ret + = ( msg_len - left ) ;
* _vector = vector ;
* _count = count ;
return 0 ;
}
static void tstream_npa_readv_msg_mode_handler ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
int ret ;
int sys_errno ;
ret = tstream_readv_pdu_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
/*
* we do not set state - > ret here as ret includes the headr size .
* we set it in tstream_npa_readv_pdu_next_vector ( )
*/
tevent_req_done ( req ) ;
}
static int tstream_npa_readv_recv ( struct tevent_req * req ,
int * perrno )
{
struct tstream_npa_readv_state * state = tevent_req_data ( req ,
struct tstream_npa_readv_state ) ;
int ret ;
ret = tsocket_simple_int_recv ( req , perrno ) ;
if ( ret = = 0 ) {
ret = state - > ret ;
}
tevent_req_received ( req ) ;
return ret ;
}
struct tstream_npa_writev_state {
const struct iovec * vector ;
size_t count ;
/* the header for message mode */
2010-04-28 12:43:19 +04:00
bool hdr_used ;
2009-03-12 20:10:19 +03:00
uint8_t hdr [ 2 ] ;
int ret ;
} ;
static void tstream_npa_writev_handler ( struct tevent_req * subreq ) ;
static struct tevent_req * tstream_npa_writev_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tstream_context * stream ,
const struct iovec * vector ,
size_t count )
{
struct tevent_req * req ;
struct tstream_npa_writev_state * state ;
struct tstream_npa * npas = tstream_context_data ( stream , struct tstream_npa ) ;
struct tevent_req * subreq ;
size_t msg_len ;
size_t i ;
struct iovec * new_vector ;
req = tevent_req_create ( mem_ctx , & state ,
struct tstream_npa_writev_state ) ;
if ( ! req ) {
return NULL ;
}
state - > ret = 0 ;
if ( ! npas - > unix_stream ) {
tevent_req_error ( req , ENOTCONN ) ;
goto post ;
}
switch ( npas - > file_type ) {
case FILE_TYPE_BYTE_MODE_PIPE :
2010-04-28 12:43:19 +04:00
state - > hdr_used = false ;
2009-03-12 20:10:19 +03:00
state - > vector = vector ;
state - > count = count ;
break ;
case FILE_TYPE_MESSAGE_MODE_PIPE :
/*
* we make a copy of the vector and prepend a header
* with the length
*/
new_vector = talloc_array ( state , struct iovec , count + 1 ) ;
if ( tevent_req_nomem ( new_vector , req ) ) {
goto post ;
}
2010-09-11 00:22:14 +04:00
new_vector [ 0 ] . iov_base = ( char * ) state - > hdr ;
2009-03-12 20:10:19 +03:00
new_vector [ 0 ] . iov_len = sizeof ( state - > hdr ) ;
memcpy ( new_vector + 1 , vector , sizeof ( struct iovec ) * count ) ;
2010-04-28 12:43:19 +04:00
state - > hdr_used = true ;
2009-03-12 20:10:19 +03:00
state - > vector = new_vector ;
state - > count = count + 1 ;
msg_len = 0 ;
for ( i = 0 ; i < count ; i + + ) {
msg_len + = vector [ i ] . iov_len ;
}
if ( msg_len > UINT16_MAX ) {
tevent_req_error ( req , EMSGSIZE ) ;
goto post ;
}
SSVAL ( state - > hdr , 0 , msg_len ) ;
break ;
}
subreq = tstream_writev_send ( state ,
ev ,
npas - > unix_stream ,
state - > vector ,
state - > count ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
goto post ;
}
tevent_req_set_callback ( subreq , tstream_npa_writev_handler , req ) ;
return req ;
post :
tevent_req_post ( req , ev ) ;
return req ;
}
static void tstream_npa_writev_handler ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_npa_writev_state * state = tevent_req_data ( req ,
struct tstream_npa_writev_state ) ;
int ret ;
int sys_errno ;
ret = tstream_writev_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
2010-04-28 12:43:19 +04:00
/*
* in message mode we need to hide the length
* of the hdr from the caller
*/
if ( state - > hdr_used ) {
ret - = sizeof ( state - > hdr ) ;
}
2009-03-12 20:10:19 +03:00
state - > ret = ret ;
tevent_req_done ( req ) ;
}
static int tstream_npa_writev_recv ( struct tevent_req * req ,
int * perrno )
{
struct tstream_npa_writev_state * state = tevent_req_data ( req ,
struct tstream_npa_writev_state ) ;
int ret ;
ret = tsocket_simple_int_recv ( req , perrno ) ;
if ( ret = = 0 ) {
ret = state - > ret ;
}
tevent_req_received ( req ) ;
return ret ;
}
struct tstream_npa_disconnect_state {
struct tstream_context * stream ;
} ;
static void tstream_npa_disconnect_handler ( struct tevent_req * subreq ) ;
static struct tevent_req * tstream_npa_disconnect_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tstream_context * stream )
{
struct tstream_npa * npas = tstream_context_data ( stream , struct tstream_npa ) ;
struct tevent_req * req ;
struct tstream_npa_disconnect_state * state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct tstream_npa_disconnect_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > stream = stream ;
if ( ! npas - > unix_stream ) {
tevent_req_error ( req , ENOTCONN ) ;
goto post ;
}
subreq = tstream_disconnect_send ( state ,
ev ,
npas - > unix_stream ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
goto post ;
}
tevent_req_set_callback ( subreq , tstream_npa_disconnect_handler , req ) ;
return req ;
post :
tevent_req_post ( req , ev ) ;
return req ;
}
static void tstream_npa_disconnect_handler ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_npa_disconnect_state * state = tevent_req_data ( req ,
struct tstream_npa_disconnect_state ) ;
struct tstream_context * stream = state - > stream ;
struct tstream_npa * npas = tstream_context_data ( stream , struct tstream_npa ) ;
int ret ;
int sys_errno ;
ret = tstream_disconnect_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
TALLOC_FREE ( npas - > unix_stream ) ;
tevent_req_done ( req ) ;
}
static int tstream_npa_disconnect_recv ( struct tevent_req * req ,
int * perrno )
{
int ret ;
ret = tsocket_simple_int_recv ( req , perrno ) ;
tevent_req_received ( req ) ;
return ret ;
}
static const struct tstream_context_ops tstream_npa_ops = {
. name = " npa " ,
. pending_bytes = tstream_npa_pending_bytes ,
. readv_send = tstream_npa_readv_send ,
. readv_recv = tstream_npa_readv_recv ,
. writev_send = tstream_npa_writev_send ,
. writev_recv = tstream_npa_writev_recv ,
. disconnect_send = tstream_npa_disconnect_send ,
. disconnect_recv = tstream_npa_disconnect_recv ,
} ;
int _tstream_npa_existing_socket ( TALLOC_CTX * mem_ctx ,
int fd ,
uint16_t file_type ,
struct tstream_context * * _stream ,
const char * location )
{
struct tstream_context * stream ;
struct tstream_npa * npas ;
int ret ;
switch ( file_type ) {
case FILE_TYPE_BYTE_MODE_PIPE :
break ;
case FILE_TYPE_MESSAGE_MODE_PIPE :
break ;
default :
errno = EINVAL ;
return - 1 ;
}
stream = tstream_context_create ( mem_ctx ,
& tstream_npa_ops ,
& npas ,
struct tstream_npa ,
location ) ;
if ( ! stream ) {
return - 1 ;
}
ZERO_STRUCTP ( npas ) ;
npas - > file_type = file_type ;
ret = tstream_bsd_existing_socket ( stream , fd ,
& npas - > unix_stream ) ;
if ( ret = = - 1 ) {
int saved_errno = errno ;
talloc_free ( stream ) ;
errno = saved_errno ;
return - 1 ;
}
* _stream = stream ;
return 0 ;
}
2010-05-20 01:02:52 +04:00
struct tstream_npa_accept_state {
struct tevent_context * ev ;
struct tstream_context * plain ;
uint16_t file_type ;
uint16_t device_state ;
uint64_t alloc_size ;
DATA_BLOB npa_blob ;
struct iovec out_iov ;
/* results */
NTSTATUS accept_status ;
struct tsocket_address * client ;
char * client_name ;
struct tsocket_address * server ;
char * server_name ;
2011-02-09 06:22:16 +03:00
struct auth_session_info_transport * session_info ;
2010-05-20 01:02:52 +04:00
} ;
static int tstream_npa_accept_next_vector ( struct tstream_context * unix_stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count ) ;
static void tstream_npa_accept_existing_reply ( struct tevent_req * subreq ) ;
static void tstream_npa_accept_existing_done ( struct tevent_req * subreq ) ;
struct tevent_req * tstream_npa_accept_existing_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tstream_context * plain ,
uint16_t file_type ,
uint16_t device_state ,
uint64_t allocation_size )
{
struct tstream_npa_accept_state * state ;
struct tevent_req * req , * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct tstream_npa_accept_state ) ;
if ( req = = NULL ) {
return NULL ;
}
switch ( file_type ) {
case FILE_TYPE_BYTE_MODE_PIPE :
break ;
case FILE_TYPE_MESSAGE_MODE_PIPE :
break ;
default :
tevent_req_error ( req , EINVAL ) ;
goto post ;
}
ZERO_STRUCTP ( state ) ;
state - > ev = ev ;
state - > plain = plain ;
state - > file_type = file_type ;
state - > device_state = device_state ;
state - > alloc_size = allocation_size ;
/*
* The named pipe pdu ' s have the length as 8 byte ( initial_read_size ) ,
* named_pipe_full_request provides the pdu length then .
*/
subreq = tstream_readv_pdu_send ( state , ev , plain ,
tstream_npa_accept_next_vector ,
state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
goto post ;
}
tevent_req_set_callback ( subreq ,
tstream_npa_accept_existing_reply , req ) ;
return req ;
post :
tevent_req_post ( req , ev ) ;
return req ;
}
static int tstream_npa_accept_next_vector ( struct tstream_context * unix_stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count )
{
struct tstream_npa_accept_state * state =
talloc_get_type_abort ( private_data ,
struct tstream_npa_accept_state ) ;
struct iovec * vector ;
size_t count ;
off_t ofs = 0 ;
if ( state - > npa_blob . length = = 0 ) {
state - > npa_blob = data_blob_talloc ( state , NULL , 4 ) ;
if ( ! state - > npa_blob . data ) {
return - 1 ;
}
} else if ( state - > npa_blob . length = = 4 ) {
uint32_t msg_len ;
ofs = 4 ;
msg_len = RIVAL ( state - > npa_blob . data , 0 ) ;
if ( msg_len > 0x00FFFFFF ) {
errno = EMSGSIZE ;
return - 1 ;
}
if ( msg_len = = 0 ) {
errno = EMSGSIZE ;
return - 1 ;
}
msg_len + = ofs ;
state - > npa_blob . data = talloc_realloc ( state ,
state - > npa_blob . data ,
uint8_t , msg_len ) ;
if ( ! state - > npa_blob . data ) {
return - 1 ;
}
state - > npa_blob . length = msg_len ;
} else {
if ( memcmp ( & state - > npa_blob . data [ 4 ] ,
NAMED_PIPE_AUTH_MAGIC , 4 ) ! = 0 ) {
DEBUG ( 0 , ( " Wrong protocol \n " ) ) ;
# if defined(EPROTONOSUPPORT)
errno = EPROTONOSUPPORT ;
# elif defined(EPROTO)
errno = EPROTO ;
# else
errno = EINVAL ;
# endif
return - 1 ;
}
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
/* we need to get a message header */
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( ! vector ) {
return - 1 ;
}
2010-09-11 00:22:14 +04:00
vector [ 0 ] . iov_base = ( char * ) ( state - > npa_blob . data + ofs ) ;
2010-05-20 01:02:52 +04:00
vector [ 0 ] . iov_len = state - > npa_blob . length - ofs ;
count = 1 ;
* _vector = vector ;
* _count = count ;
return 0 ;
}
static void tstream_npa_accept_existing_reply ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq , struct tevent_req ) ;
struct tstream_npa_accept_state * state =
tevent_req_data ( req , struct tstream_npa_accept_state ) ;
struct named_pipe_auth_req * pipe_request ;
struct named_pipe_auth_rep pipe_reply ;
2011-02-09 06:22:16 +03:00
struct named_pipe_auth_req_info4 i4 ;
2010-05-20 01:02:52 +04:00
enum ndr_err_code ndr_err ;
DATA_BLOB out ;
int sys_errno ;
int ret ;
ret = tstream_readv_pdu_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
DEBUG ( 10 , ( " Received packet of length %lu \n " ,
( long ) state - > npa_blob . length ) ) ;
dump_data ( 11 , state - > npa_blob . data , state - > npa_blob . length ) ;
ZERO_STRUCT ( pipe_reply ) ;
pipe_reply . level = 0 ;
pipe_reply . status = NT_STATUS_INTERNAL_ERROR ;
/*
* TODO : check it ' s a root ( uid = = 0 ) pipe
*/
pipe_request = talloc ( state , struct named_pipe_auth_req ) ;
if ( ! pipe_request ) {
DEBUG ( 0 , ( " Out of memory! \n " ) ) ;
goto reply ;
}
/* parse the passed credentials */
ndr_err = ndr_pull_struct_blob_all (
& state - > npa_blob , pipe_request , pipe_request ,
( ndr_pull_flags_fn_t ) ndr_pull_named_pipe_auth_req ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
pipe_reply . status = ndr_map_error2ntstatus ( ndr_err ) ;
DEBUG ( 2 , ( " Could not unmarshall named_pipe_auth_req: %s \n " ,
nt_errstr ( pipe_reply . status ) ) ) ;
goto reply ;
}
if ( DEBUGLVL ( 10 ) ) {
NDR_PRINT_DEBUG ( named_pipe_auth_req , pipe_request ) ;
}
2011-02-09 06:22:16 +03:00
ZERO_STRUCT ( i4 ) ;
2010-05-20 01:02:52 +04:00
2011-02-09 06:22:16 +03:00
if ( pipe_request - > level ! = 4 ) {
2010-05-20 01:02:52 +04:00
DEBUG ( 0 , ( " Unknown level %u \n " , pipe_request - > level ) ) ;
pipe_reply . level = 0 ;
pipe_reply . status = NT_STATUS_INVALID_LEVEL ;
goto reply ;
}
2011-02-09 06:22:16 +03:00
pipe_reply . level = 4 ;
2011-02-09 03:59:50 +03:00
pipe_reply . status = NT_STATUS_OK ;
2011-02-09 06:22:16 +03:00
pipe_reply . info . info4 . file_type = state - > file_type ;
pipe_reply . info . info4 . device_state = state - > device_state ;
pipe_reply . info . info4 . allocation_size = state - > alloc_size ;
2010-05-20 01:02:52 +04:00
2011-02-09 06:22:16 +03:00
i4 = pipe_request - > info . info4 ;
if ( i4 . server_addr = = NULL ) {
2011-02-09 03:59:50 +03:00
pipe_reply . status = NT_STATUS_INVALID_ADDRESS ;
DEBUG ( 2 , ( " Missing server address \n " ) ) ;
goto reply ;
}
2011-02-09 06:22:16 +03:00
if ( i4 . client_addr = = NULL ) {
2011-02-09 03:59:50 +03:00
pipe_reply . status = NT_STATUS_INVALID_ADDRESS ;
DEBUG ( 2 , ( " Missing client address \n " ) ) ;
goto reply ;
}
2010-05-20 01:02:52 +04:00
2011-02-09 03:59:50 +03:00
state - > server_name = discard_const_p ( char ,
2011-02-09 06:22:16 +03:00
talloc_move ( state , & i4 . server_name ) ) ;
2011-02-09 03:59:50 +03:00
ret = tsocket_address_inet_from_strings ( state , " ip " ,
2011-02-09 06:22:16 +03:00
i4 . server_addr ,
i4 . server_port ,
2011-02-09 03:59:50 +03:00
& state - > server ) ;
if ( ret ! = 0 ) {
DEBUG ( 2 , ( " Invalid server address[%s:%u] - %s \n " ,
2011-02-09 06:22:16 +03:00
i4 . server_addr , i4 . server_port ,
2011-02-09 03:59:50 +03:00
strerror ( errno ) ) ) ;
pipe_reply . status = NT_STATUS_INVALID_ADDRESS ;
goto reply ;
2010-05-20 01:02:52 +04:00
}
2011-02-09 03:59:50 +03:00
state - > client_name = discard_const_p ( char ,
2011-02-09 06:22:16 +03:00
talloc_move ( state , & i4 . client_name ) ) ;
2011-02-09 03:59:50 +03:00
ret = tsocket_address_inet_from_strings ( state , " ip " ,
2011-02-09 06:22:16 +03:00
i4 . client_addr ,
i4 . client_port ,
2011-02-09 03:59:50 +03:00
& state - > client ) ;
if ( ret ! = 0 ) {
DEBUG ( 2 , ( " Invalid server address[%s:%u] - %s \n " ,
2011-02-09 06:22:16 +03:00
i4 . client_addr , i4 . client_port ,
2011-02-09 03:59:50 +03:00
strerror ( errno ) ) ) ;
pipe_reply . status = NT_STATUS_INVALID_ADDRESS ;
goto reply ;
2010-05-20 01:02:52 +04:00
}
2011-02-09 06:22:16 +03:00
state - > session_info = talloc_move ( state , & i4 . session_info ) ;
2010-05-20 01:02:52 +04:00
reply :
/* create the output */
ndr_err = ndr_push_struct_blob ( & out , state , & pipe_reply ,
( ndr_push_flags_fn_t ) ndr_push_named_pipe_auth_rep ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 2 , ( " Error encoding structure: %s " ,
ndr_map_error2string ( ndr_err ) ) ) ;
tevent_req_error ( req , EIO ) ;
return ;
}
DEBUG ( 10 , ( " named_pipe_auth reply[%u] \n " , ( unsigned ) out . length ) ) ;
dump_data ( 11 , out . data , out . length ) ;
if ( DEBUGLVL ( 10 ) ) {
NDR_PRINT_DEBUG ( named_pipe_auth_rep , & pipe_reply ) ;
}
state - > accept_status = pipe_reply . status ;
2010-09-11 00:22:14 +04:00
state - > out_iov . iov_base = ( char * ) out . data ;
2010-05-20 01:02:52 +04:00
state - > out_iov . iov_len = out . length ;
subreq = tstream_writev_send ( state , state - > ev ,
state - > plain ,
& state - > out_iov , 1 ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
DEBUG ( 0 , ( " no memory for tstream_writev_send " ) ) ;
return ;
}
tevent_req_set_callback ( subreq , tstream_npa_accept_existing_done , req ) ;
}
static void tstream_npa_accept_existing_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq , struct tevent_req ) ;
int sys_errno ;
int ret ;
ret = tstream_writev_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
tevent_req_done ( req ) ;
}
int _tstream_npa_accept_existing_recv ( struct tevent_req * req ,
int * perrno ,
TALLOC_CTX * mem_ctx ,
struct tstream_context * * stream ,
struct tsocket_address * * client ,
char * * _client_name ,
struct tsocket_address * * server ,
char * * server_name ,
2011-02-09 06:22:16 +03:00
struct auth_session_info_transport * * session_info ,
2010-05-20 01:02:52 +04:00
const char * location )
{
struct tstream_npa_accept_state * state =
tevent_req_data ( req , struct tstream_npa_accept_state ) ;
struct tstream_npa * npas ;
int ret ;
ret = tsocket_simple_int_recv ( req , perrno ) ;
if ( ret ! = 0 ) {
DEBUG ( 2 , ( " Failed to accept named pipe conection: %s \n " ,
strerror ( * perrno ) ) ) ;
tevent_req_received ( req ) ;
return - 1 ;
}
if ( ! NT_STATUS_IS_OK ( state - > accept_status ) ) {
# if defined(EPROTONOSUPPORT)
* perrno = EPROTONOSUPPORT ;
# elif defined(EPROTO)
* perrno = EPROTO ;
# else
* perrno = EINVAL ;
# endif
DEBUG ( 2 , ( " Failed to accept named pipe conection: %s => %s \n " ,
nt_errstr ( state - > accept_status ) ,
strerror ( * perrno ) ) ) ;
tevent_req_received ( req ) ;
return - 1 ;
}
* stream = tstream_context_create ( mem_ctx ,
& tstream_npa_ops ,
& npas ,
struct tstream_npa ,
location ) ;
if ( ! * stream ) {
* perrno = ENOMEM ;
tevent_req_received ( req ) ;
return - 1 ;
}
ZERO_STRUCTP ( npas ) ;
npas - > unix_stream = state - > plain ;
npas - > file_type = state - > file_type ;
* client = talloc_move ( mem_ctx , & state - > client ) ;
* _client_name = talloc_move ( mem_ctx , & state - > client_name ) ;
* server = talloc_move ( mem_ctx , & state - > server ) ;
* server_name = talloc_move ( mem_ctx , & state - > server_name ) ;
2011-02-09 06:22:16 +03:00
* session_info = talloc_move ( mem_ctx , & state - > session_info ) ;
2010-05-20 01:02:52 +04:00
tevent_req_received ( req ) ;
return 0 ;
}