2014-09-16 20:05:53 +04:00
/*
Unix SMB / CIFS implementation .
HTTP library
Copyright ( C ) 2014 Samuel Cabrero < samuelcabrero @ kernevil . me >
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 "http.h"
# include "http_internal.h"
# include "lib/util/tevent_ntstatus.h"
# include "lib/param/param.h"
# include "tevent.h"
# include "auth/gensec/gensec.h"
# include "auth/credentials/credentials.h"
# include "lib/util/data_blob.h"
/**
* Copy the request headers from src to dst
*/
2017-05-11 16:34:08 +03:00
static NTSTATUS http_copy_header ( const struct http_request * src ,
2014-09-16 20:05:53 +04:00
struct http_request * dst )
{
struct http_header * h ;
dst - > type = src - > type ;
dst - > major = src - > major ;
dst - > minor = src - > minor ;
dst - > uri = talloc_strdup ( dst , src - > uri ) ;
for ( h = src - > headers ; h ! = NULL ; h = h - > next ) {
http_add_header ( dst , & dst - > headers , h - > key , h - > value ) ;
}
dst - > headers_size = src - > headers_size ;
return NT_STATUS_OK ;
}
/*
* Retrieve the WWW - Authenticate header from server response based on the
* authentication scheme being used .
*/
2017-07-20 16:48:35 +03:00
static NTSTATUS http_parse_auth_response ( const DATA_BLOB prefix ,
2014-09-16 20:05:53 +04:00
struct http_request * auth_response ,
DATA_BLOB * in )
{
struct http_header * h ;
for ( h = auth_response - > headers ; h ! = NULL ; h = h - > next ) {
2017-07-20 16:46:38 +03:00
int cmp ;
cmp = strcasecmp ( h - > key , " WWW-Authenticate " ) ;
if ( cmp ! = 0 ) {
2017-07-20 15:44:51 +03:00
continue ;
}
2017-07-20 16:48:35 +03:00
cmp = strncasecmp ( h - > value ,
( const char * ) prefix . data ,
prefix . length ) ;
if ( cmp ! = 0 ) {
continue ;
2014-09-16 20:05:53 +04:00
}
2017-07-20 16:48:35 +03:00
* in = data_blob_string_const ( h - > value ) ;
return NT_STATUS_OK ;
2014-09-16 20:05:53 +04:00
}
return NT_STATUS_NOT_SUPPORTED ;
}
2017-05-11 16:34:08 +03:00
struct http_auth_state {
struct tevent_context * ev ;
2014-09-16 20:05:53 +04:00
2019-08-06 13:33:40 +03:00
struct http_conn * http_conn ;
2014-09-16 20:05:53 +04:00
2017-05-11 16:34:08 +03:00
enum http_auth_method auth ;
2017-07-20 16:48:35 +03:00
DATA_BLOB prefix ;
2014-09-16 20:05:53 +04:00
2017-05-11 16:34:08 +03:00
struct gensec_security * gensec_ctx ;
NTSTATUS gensec_status ;
2014-09-16 20:05:53 +04:00
2017-05-11 16:34:08 +03:00
const struct http_request * original_request ;
struct http_request * next_request ;
struct http_request * auth_response ;
2014-09-16 20:05:53 +04:00
} ;
2017-05-11 16:34:08 +03:00
static void http_send_auth_request_gensec_done ( struct tevent_req * subreq ) ;
static void http_send_auth_request_http_req_done ( struct tevent_req * subreq ) ;
static void http_send_auth_request_http_rep_done ( struct tevent_req * subreq ) ;
2014-09-16 20:05:53 +04:00
struct tevent_req * http_send_auth_request_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2019-08-06 13:33:40 +03:00
struct http_conn * http_conn ,
2017-05-11 16:34:08 +03:00
const struct http_request * original_request ,
2014-09-16 20:05:53 +04:00
struct cli_credentials * credentials ,
struct loadparm_context * lp_ctx ,
enum http_auth_method auth )
{
2017-05-11 16:34:08 +03:00
struct tevent_req * req = NULL ;
struct http_auth_state * state = NULL ;
struct tevent_req * subreq = NULL ;
DATA_BLOB gensec_in = data_blob_null ;
2014-09-16 20:05:53 +04:00
NTSTATUS status ;
2017-07-20 17:11:48 +03:00
struct http_header * h = NULL ;
2017-05-11 16:34:08 +03:00
const char * mech_name = NULL ;
2014-09-16 20:05:53 +04:00
req = tevent_req_create ( mem_ctx , & state , struct http_auth_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
2019-08-06 13:33:40 +03:00
state - > http_conn = http_conn ;
2014-09-16 20:05:53 +04:00
state - > auth = auth ;
2017-05-11 16:34:08 +03:00
state - > original_request = original_request ;
2014-09-16 20:05:53 +04:00
status = gensec_init ( ) ;
2017-05-11 16:34:08 +03:00
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
2014-09-16 20:05:53 +04:00
}
2017-05-11 16:34:08 +03:00
2014-09-16 20:05:53 +04:00
status = gensec_client_start ( state , & state - > gensec_ctx ,
lpcfg_gensec_settings ( state , lp_ctx ) ) ;
2017-05-11 16:34:08 +03:00
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
2014-09-16 20:05:53 +04:00
}
2017-05-11 16:34:08 +03:00
2014-09-16 20:05:53 +04:00
status = gensec_set_credentials ( state - > gensec_ctx , credentials ) ;
2017-05-11 16:34:08 +03:00
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
2014-09-16 20:05:53 +04:00
}
2017-07-20 17:11:48 +03:00
for ( h = original_request - > headers ; h ! = NULL ; h = h - > next ) {
int cmp ;
cmp = strcasecmp ( h - > key , " Host " ) ;
if ( cmp ! = 0 ) {
continue ;
}
status = gensec_set_target_service ( state - > gensec_ctx , " http " ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
status = gensec_set_target_hostname ( state - > gensec_ctx , h - > value ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
break ;
}
2014-09-16 20:05:53 +04:00
switch ( state - > auth ) {
case HTTP_AUTH_BASIC :
2017-05-11 16:34:08 +03:00
mech_name = " http_basic " ;
2017-07-20 16:48:35 +03:00
state - > prefix = data_blob_string_const ( " Basic " ) ;
2014-09-16 20:05:53 +04:00
break ;
case HTTP_AUTH_NTLM :
2017-05-11 16:34:08 +03:00
mech_name = " http_ntlm " ;
2017-07-20 16:48:35 +03:00
state - > prefix = data_blob_string_const ( " NTLM " ) ;
2014-09-16 20:05:53 +04:00
break ;
2017-07-20 14:03:40 +03:00
case HTTP_AUTH_NEGOTIATE :
mech_name = " http_negotiate " ;
state - > prefix = data_blob_string_const ( " Negotiate " ) ;
break ;
2014-09-16 20:05:53 +04:00
default :
tevent_req_nterror ( req , NT_STATUS_NOT_SUPPORTED ) ;
return tevent_req_post ( req , ev ) ;
}
2017-05-11 16:34:08 +03:00
status = gensec_start_mech_by_name ( state - > gensec_ctx , mech_name ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
2014-09-16 20:05:53 +04:00
}
2017-05-11 16:34:08 +03:00
subreq = gensec_update_send ( state , state - > ev ,
state - > gensec_ctx ,
gensec_in ) ;
2014-09-16 20:05:53 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2017-05-11 16:34:08 +03:00
tevent_req_set_callback ( subreq , http_send_auth_request_gensec_done , req ) ;
2014-09-16 20:05:53 +04:00
return req ;
}
2017-05-11 16:34:08 +03:00
static void http_send_auth_request_gensec_done ( struct tevent_req * subreq )
2014-09-16 20:05:53 +04:00
{
2017-05-11 16:34:08 +03:00
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct http_auth_state * state =
tevent_req_data ( req ,
struct http_auth_state ) ;
DATA_BLOB gensec_out = data_blob_null ;
NTSTATUS status ;
int ret ;
2014-09-16 20:05:53 +04:00
2017-05-11 16:34:08 +03:00
TALLOC_FREE ( state - > auth_response ) ;
2014-09-16 20:05:53 +04:00
2017-05-11 16:34:08 +03:00
status = gensec_update_recv ( subreq , state , & gensec_out ) ;
2014-09-16 20:05:53 +04:00
TALLOC_FREE ( subreq ) ;
2017-05-11 16:34:08 +03:00
state - > gensec_status = status ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
status = NT_STATUS_OK ;
}
2014-09-16 20:05:53 +04:00
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2017-05-11 16:34:08 +03:00
state - > next_request = talloc_zero ( state , struct http_request ) ;
if ( tevent_req_nomem ( state - > next_request , req ) ) {
2014-09-16 20:05:53 +04:00
return ;
}
2017-05-11 16:34:08 +03:00
status = http_copy_header ( state - > original_request , state - > next_request ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
if ( ! NT_STATUS_IS_OK ( state - > gensec_status ) ) {
/*
* More preprocessing required before we
* can include the content .
*/
ret = http_replace_header ( state - > next_request ,
& state - > next_request - > headers ,
" Content-Length " , " 0 " ) ;
if ( ret ! = 0 ) {
tevent_req_oom ( req ) ;
return ;
}
} else {
state - > next_request - > body = state - > original_request - > body ;
}
if ( gensec_out . length > 0 ) {
ret = http_add_header ( state - > next_request ,
& state - > next_request - > headers ,
" Authorization " ,
( char * ) gensec_out . data ) ;
if ( ret ! = 0 ) {
tevent_req_oom ( req ) ;
2014-09-16 20:05:53 +04:00
return ;
}
2017-05-11 16:34:08 +03:00
data_blob_free ( & gensec_out ) ;
}
subreq = http_send_request_send ( state , state - > ev ,
2019-08-06 13:33:40 +03:00
state - > http_conn ,
2017-05-11 16:34:08 +03:00
state - > next_request ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
http_send_auth_request_http_req_done ,
req ) ;
}
static void http_send_auth_request_http_req_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct http_auth_state * state =
tevent_req_data ( req ,
struct http_auth_state ) ;
NTSTATUS status ;
TALLOC_FREE ( state - > next_request ) ;
status = http_send_request_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
2014-09-16 20:05:53 +04:00
return ;
}
/*
2017-05-11 16:34:08 +03:00
* If no more processing required , it is done
*
* The caller will use http_read_response_send / recv
* in order to get the high level response .
2014-09-16 20:05:53 +04:00
*/
2017-05-11 16:34:08 +03:00
if ( NT_STATUS_IS_OK ( state - > gensec_status ) ) {
tevent_req_done ( req ) ;
return ;
}
2017-07-20 19:12:27 +03:00
/*
* If more processing required , read the response from server
*
* We may get an empty RPCH Echo packet from the server
* on the " RPC_OUT_DATA " path . We need to consume this
* from the socket , but for now we just ignore the bytes .
*/
2017-05-11 16:34:08 +03:00
subreq = http_read_response_send ( state , state - > ev ,
2019-08-06 13:33:40 +03:00
state - > http_conn ,
2017-07-20 19:12:27 +03:00
UINT16_MAX ) ;
2017-05-11 16:34:08 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
http_send_auth_request_http_rep_done ,
req ) ;
2014-09-16 20:05:53 +04:00
}
2017-05-11 16:34:08 +03:00
static void http_send_auth_request_http_rep_done ( struct tevent_req * subreq )
2014-09-16 20:05:53 +04:00
{
2017-05-11 16:34:08 +03:00
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct http_auth_state * state =
tevent_req_data ( req ,
struct http_auth_state ) ;
DATA_BLOB gensec_in = data_blob_null ;
2014-09-16 20:05:53 +04:00
NTSTATUS status ;
2017-05-11 16:34:08 +03:00
status = http_read_response_recv ( subreq , state ,
& state - > auth_response ) ;
2014-09-16 20:05:53 +04:00
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2017-07-20 19:12:27 +03:00
/*
* We we asked for up to UINT16_MAX bytes of
* content , we don ' t expect
* state - > auth_response - > remaining_content_length
* to be set .
*
* For now we just ignore any bytes in
* state - > auth_response - > body .
*/
if ( state - > auth_response - > remaining_content_length ! = 0 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
2017-07-20 16:48:35 +03:00
status = http_parse_auth_response ( state - > prefix ,
2017-05-11 16:34:08 +03:00
state - > auth_response ,
& gensec_in ) ;
if ( tevent_req_nterror ( req , status ) ) {
2014-09-16 20:05:53 +04:00
return ;
}
2017-05-11 16:34:08 +03:00
subreq = gensec_update_send ( state , state - > ev ,
state - > gensec_ctx ,
gensec_in ) ;
2014-09-16 20:05:53 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
2017-05-11 16:34:08 +03:00
tevent_req_set_callback ( subreq , http_send_auth_request_gensec_done , req ) ;
2014-09-16 20:05:53 +04:00
}
NTSTATUS http_send_auth_request_recv ( struct tevent_req * req )
{
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}