/*-
* SSLsplit - transparent SSL / TLS interception
* https : //www.roe.ch/SSLsplit
*
* Copyright ( c ) 2009 - 2019 , Daniel Roethlisberger < daniel @ roe . ch > .
* Copyright ( c ) 2017 - 2024 , Soner Tari < sonertari @ gmail . com > .
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
* 1. Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ` ` AS IS ' '
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
# include "pxyconn.h"
# include "prototcp.h"
# include "protossl.h"
# include "protohttp.h"
# include "protopop3.h"
# include "protosmtp.h"
# include "protoautossl.h"
# include "protopassthrough.h"
# include "privsep.h"
# include "sys.h"
# include "log.h"
# include "attrib.h"
# include "proc.h"
# include "util.h"
# include <string.h>
# include <arpa/inet.h>
# include <sys/param.h>
# include <assert.h>
# include <event2/listener.h>
# ifdef __linux__
# include <glob.h>
# endif /* __linux__ */
# include <net/if_arp.h>
# include <sys/ioctl.h>
# include <sys/socket.h>
# if (__linux__ && HAVE_SYSCTL) || !__linux__
# include <sys/sysctl.h>
# endif
# include <net/route.h>
# include <netinet/if_ether.h>
# ifdef __OpenBSD__
# include <net/if_dl.h>
# endif /* __OpenBSD__ */
// getdtablecount() returns int, hence we don't use size_t here
int descriptor_table_size = 0 ;
// @attention The order of names should match the order in protocol enum
char * protocol_names [ ] = {
// ERROR = -1
" PASSTHROUGH " , // = 0
" HTTP " ,
" HTTPS " ,
" POP3 " ,
" POP3S " ,
" SMTP " ,
" SMTPS " ,
" AUTOSSL " ,
" TCP " ,
" SSL " ,
} ;
static protocol_t NONNULL ( 1 )
pxy_setup_proto_child ( pxy_conn_child_ctx_t * ctx )
{
ctx - > protoctx = malloc ( sizeof ( proto_child_ctx_t ) ) ;
if ( ! ctx - > protoctx ) {
return PROTO_ERROR ;
}
memset ( ctx - > protoctx , 0 , sizeof ( proto_child_ctx_t ) ) ;
// Default to tcp
prototcp_setup_child ( ctx ) ;
protocol_t proto ;
if ( ctx - > conn - > spec - > upgrade ) {
proto = protoautossl_setup_child ( ctx ) ;
} else if ( ctx - > conn - > spec - > http ) {
if ( ctx - > conn - > spec - > ssl ) {
proto = protohttps_setup_child ( ctx ) ;
} else {
proto = protohttp_setup_child ( ctx ) ;
}
} else if ( ctx - > conn - > spec - > pop3 ) {
if ( ctx - > conn - > spec - > ssl ) {
proto = ( protossl_setup_child ( ctx ) ! = PROTO_ERROR ) ? PROTO_POP3S : PROTO_ERROR ;
} else {
proto = PROTO_POP3 ;
}
} else if ( ctx - > conn - > spec - > smtp ) {
if ( ctx - > conn - > spec - > ssl ) {
proto = ( protossl_setup_child ( ctx ) ! = PROTO_ERROR ) ? PROTO_SMTPS : PROTO_ERROR ;
} else {
proto = PROTO_SMTP ;
}
} else if ( ctx - > conn - > spec - > ssl ) {
proto = protossl_setup_child ( ctx ) ;
} else {
proto = PROTO_TCP ;
}
if ( proto = = PROTO_ERROR ) {
free ( ctx - > protoctx ) ;
}
return proto ;
}
static pxy_conn_child_ctx_t * MALLOC NONNULL ( 2 )
pxy_conn_ctx_new_child ( evutil_socket_t fd , pxy_conn_ctx_t * ctx )
{
assert ( ctx ! = NULL ) ;
log_finest_va ( " ENTER, fd=%d " , fd ) ;
pxy_conn_child_ctx_t * child_ctx = malloc ( sizeof ( pxy_conn_child_ctx_t ) ) ;
if ( ! child_ctx ) {
return NULL ;
}
memset ( child_ctx , 0 , sizeof ( pxy_conn_child_ctx_t ) ) ;
child_ctx - > type = CONN_TYPE_CHILD ;
# ifdef DEBUG_PROXY
child_ctx - > id = ctx - > child_count + + ;
# endif /* DEBUG_PROXY */
child_ctx - > conn = ctx ;
child_ctx - > fd = fd ;
if ( pxy_setup_proto_child ( child_ctx ) = = PROTO_ERROR ) {
free ( child_ctx ) ;
return NULL ;
}
return child_ctx ;
}
static void NONNULL ( 1 )
pxy_conn_ctx_free_child ( pxy_conn_child_ctx_t * ctx )
{
log_finest ( " ENTER " ) ;
// If the proto doesn't have special args, proto_free() callback is NULL
if ( ctx - > protoctx - > proto_free ) {
ctx - > protoctx - > proto_free ( ctx ) ;
}
free ( ctx - > protoctx ) ;
free ( ctx ) ;
}
// This function cannot fail.
static void NONNULL ( 1 )
pxy_conn_attach_child ( pxy_conn_child_ctx_t * ctx )
{
log_finest ( " Adding child conn " ) ;
// @attention Child connections use the parent's event bases, otherwise we would get multithreading issues
// Always keep thr load and conns list in sync
ctx - > conn - > thr - > load + + ;
ctx - > conn - > thr - > max_load = max ( ctx - > conn - > thr - > max_load , ctx - > conn - > thr - > load ) ;
// Prepend child to the children list of parent
ctx - > next = ctx - > conn - > children ;
ctx - > conn - > children = ctx ;
if ( ctx - > next )
ctx - > next - > prev = ctx ;
}
// This function cannot fail.
static void NONNULL ( 1 )
pxy_conn_detach_child ( pxy_conn_child_ctx_t * ctx )
{
assert ( ctx - > conn ! = NULL ) ;
assert ( ctx - > conn - > children ! = NULL ) ;
log_finest ( " Removing child conn " ) ;
ctx - > conn - > thr - > load - - ;
if ( ctx - > prev ) {
ctx - > prev - > next = ctx - > next ;
} else {
ctx - > conn - > children = ctx - > next ;
}
if ( ctx - > next )
ctx - > next - > prev = ctx - > prev ;
# ifdef DEBUG_PROXY
if ( ctx - > conn - > children ) {
if ( ctx - > id = = ctx - > conn - > children - > id ) {
// This should never happen
log_fine ( " Found child in conn children, first " ) ;
assert ( 0 ) ;
} else {
pxy_conn_child_ctx_t * current = ctx - > conn - > children - > next ;
pxy_conn_child_ctx_t * previous = ctx - > conn - > children ;
while ( current ! = NULL & & previous ! = NULL ) {
if ( ctx - > id = = current - > id ) {
// This should never happen
log_fine ( " Found child in conn children " ) ;
assert ( 0 ) ;
}
previous = current ;
current = current - > next ;
}
log_finest ( " Cannot find child in conn children " ) ;
}
} else {
log_finest ( " Cannot find child in conn children, empty " ) ;
}
# endif /* DEBUG_PROXY */
}
static void
pxy_conn_free_child ( pxy_conn_child_ctx_t * ctx )
{
assert ( ctx - > conn ! = NULL ) ;
log_finest ( " ENTER " ) ;
// We always assign NULL to bevs after freeing them
if ( ctx - > src . bev ) {
ctx - > src . free ( ctx - > src . bev , ctx - > conn ) ;
ctx - > src . bev = NULL ;
} else if ( ! ctx - > src . closed ) {
log_fine ( " !src.closed, evutil_closesocket on NULL src.bev " ) ;
// @attention early in the conn setup, src fd may be open, although src.bev is NULL
evutil_closesocket ( ctx - > fd ) ;
}
if ( ctx - > dst . bev ) {
ctx - > dst . free ( ctx - > dst . bev , ctx - > conn ) ;
ctx - > dst . bev = NULL ;
}
// Save conn before freeing ctx
pxy_conn_ctx_t * conn = ctx - > conn ;
pxy_conn_detach_child ( ctx ) ;
pxy_conn_ctx_free_child ( ctx ) ;
// If there is no child left, free child_evcl asap by calling pxy_conn_free_children()
if ( ! conn - > children )
pxy_conn_free_children ( conn ) ;
}
void
pxy_conn_term_child ( pxy_conn_child_ctx_t * ctx )
{
log_finest ( " ENTER " ) ;
ctx - > term = 1 ;
}
void
pxy_conn_free_children ( pxy_conn_ctx_t * ctx )
{
log_finest ( " ENTER " ) ;
// @attention Free the child ctxs asap, we need their fds
while ( ctx - > children ) {
pxy_conn_free_child ( ctx - > children ) ;
}
// @attention Parent may be closing before there was any child at all nor was child_evcl ever created
if ( ctx - > child_evcl ) {
log_finer_va ( " Freeing child_evcl, children fd=%d " , ctx - > children ? ctx - > children - > fd : - 1 ) ;
// @attention child_evcl was created with LEV_OPT_CLOSE_ON_FREE, so do not close ctx->child_fd
evconnlistener_free ( ctx - > child_evcl ) ;
ctx - > child_evcl = NULL ;
}
}
/*
* Does full clean - up of conn ctx .
* This is the conn handling thr version of a similar function
* proxy_conn_ctx_free ( ) , which runs on thrmgr and does minimal
* clean - up .
*/
void
pxy_conn_ctx_free ( pxy_conn_ctx_t * ctx , int by_requestor )
{
log_finest ( " ENTER " ) ;
if ( WANT_CONTENT_LOG ( ctx ) ) {
// Always try to close log files, even if content, pcap, or mirror logging is disabled by filter rules
// The log files may have been initialized and opened
// so, do not pass down the log_content, log_pcap, and log_mirror fields of ctx
if ( log_content_close ( & ctx - > logctx , by_requestor ) = = - 1 ) {
log_err_level_printf ( LOG_WARNING , " Content log close failed \n " ) ;
}
}
# ifndef WITHOUT_USERAUTH
if ( ctx - > conn_opts - > user_auth & & ctx - > srchost_str & & ctx - > user & & ctx - > ether ) {
// Update userdb atime if idle time is more than 50% of user timeout, which is expected to reduce update frequency
unsigned int idletime = ctx - > idletime + ( time ( NULL ) - ctx - > ctime ) ;
if ( idletime > ( ctx - > conn_opts - > user_timeout / 2 ) ) {
userdbkeys_t keys ;
// Zero out for NULL termination
memset ( & keys , 0 , sizeof ( userdbkeys_t ) ) ;
// Leave room for NULL to make sure the strings are always NULL terminated
strncpy ( keys . ip , ctx - > srchost_str , sizeof ( keys . ip ) - 1 ) ;
strncpy ( keys . user , ctx - > user , sizeof ( keys . user ) - 1 ) ;
strncpy ( keys . ether , ctx - > ether , sizeof ( keys . ether ) - 1 ) ;
if ( privsep_client_update_atime ( ctx - > clisock , & keys ) = = - 1 ) {
log_finest_va ( " Error updating user atime: %s " , sqlite3_errmsg ( ctx - > global - > userdb ) ) ;
} else {
log_finest ( " Successfully updated user atime " ) ;
}
} else {
log_finest_va ( " Will not update user atime, idletime=%u " , idletime ) ;
}
}
# endif /* !WITHOUT_USERAUTH */
pxy_thr_detach ( ctx ) ;
if ( ctx - > srchost_str ) {
free ( ctx - > srchost_str ) ;
}
if ( ctx - > srcport_str ) {
free ( ctx - > srcport_str ) ;
}
if ( ctx - > dsthost_str ) {
free ( ctx - > dsthost_str ) ;
}
if ( ctx - > dstport_str ) {
free ( ctx - > dstport_str ) ;
}
# ifdef HAVE_LOCAL_PROCINFO
if ( ctx - > lproc . exec_path ) {
free ( ctx - > lproc . exec_path ) ;
}
if ( ctx - > lproc . user ) {
free ( ctx - > lproc . user ) ;
}
if ( ctx - > lproc . group ) {
free ( ctx - > lproc . group ) ;
}
# endif /* HAVE_LOCAL_PROCINFO */
if ( ctx - > ev ) {
event_free ( ctx - > ev ) ;
}
if ( ctx - > sslproxy_header ) {
free ( ctx - > sslproxy_header ) ;
}
// If the proto doesn't have special args, proto_free() callback is NULL
if ( ctx - > protoctx - > proto_free ) {
ctx - > protoctx - > proto_free ( ctx ) ;
}
free ( ctx - > protoctx ) ;
# ifndef WITHOUT_USERAUTH
if ( ctx - > user ) {
free ( ctx - > user ) ;
}
if ( ctx - > ether ) {
free ( ctx - > ether ) ;
}
if ( ctx - > desc ) {
free ( ctx - > desc ) ;
}
# endif /* !WITHOUT_USERAUTH */
free ( ctx ) ;
}
void
pxy_conn_free ( pxy_conn_ctx_t * ctx , int by_requestor )
{
log_finest ( " ENTER " ) ;
// We always assign NULL to bevs after freeing them
if ( ctx - > src . bev ) {
ctx - > src . free ( ctx - > src . bev , ctx ) ;
ctx - > src . bev = NULL ;
} else if ( ! ctx - > src . closed ) {
log_fine ( " evutil_closesocket on NULL src.bev " ) ;
// @attention early in the conn setup, src fd may be open, although src.bev is NULL
evutil_closesocket ( ctx - > fd ) ;
}
if ( ctx - > srvdst . bev ) {
// In split mode, srvdst is used as dst, so it should be freed as dst below
// If srvdst has been xferred to the first child conn, the child should free it, not the parent
ctx - > srvdst . free ( ctx - > srvdst . bev , ctx ) ;
ctx - > srvdst . bev = NULL ;
}
if ( ctx - > dst . bev ) {
ctx - > dst . free ( ctx - > dst . bev , ctx ) ;
ctx - > dst . bev = NULL ;
}
pxy_conn_free_children ( ctx ) ;
pxy_conn_ctx_free ( ctx , by_requestor ) ;
}
void
pxy_conn_term ( pxy_conn_ctx_t * ctx , int by_requestor )
{
log_finest ( " ENTER " ) ;
ctx - > term = 1 ;
ctx - > term_requestor = by_requestor ;
}
void
pxy_log_connect_nonhttp ( pxy_conn_ctx_t * ctx )
{
if ( ! ctx - > log_connect )
return ;
char * msg ;
# ifdef HAVE_LOCAL_PROCINFO
char * lpi = NULL ;
# endif /* HAVE_LOCAL_PROCINFO */
int rv ;
# ifdef HAVE_LOCAL_PROCINFO
if ( ctx - > global - > lprocinfo ) {
rv = asprintf ( & lpi , " lproc:%i:%s:%s:%s " ,
ctx - > lproc . pid ,
STRORDASH ( ctx - > lproc . user ) ,
STRORDASH ( ctx - > lproc . group ) ,
STRORDASH ( ctx - > lproc . exec_path ) ) ;
if ( ( rv < 0 ) | | ! lpi ) {
ctx - > enomem = 1 ;
goto out ;
}
} else {
lpi = " " ;
}
# endif /* HAVE_LOCAL_PROCINFO */
/*
* The following ifdef ' s within asprintf arguments list generates
* warnings with - Wembedded - directive on some compilers .
* Not fixing the code in order to avoid more code duplication .
*/
if ( ! ctx - > src . ssl ) {
rv = asprintf ( & msg , " CONN: %s %s %s %s %s "
# ifdef HAVE_LOCAL_PROCINFO
" %s "
# endif /* HAVE_LOCAL_PROCINFO */
# ifndef WITHOUT_USERAUTH
" user:%s "
# endif /* !WITHOUT_USERAUTH */
" \n " ,
ctx - > proto = = PROTO_AUTOSSL ? " autossl " : ( ctx - > proto = = PROTO_PASSTHROUGH ? " passthrough " : ( ctx - > proto = = PROTO_POP3 ? " pop3 " : ( ctx - > proto = = PROTO_SMTP ? " smtp " : " tcp " ) ) ) ,
STRORDASH ( ctx - > srchost_str ) ,
STRORDASH ( ctx - > srcport_str ) ,
STRORDASH ( ctx - > dsthost_str ) ,
STRORDASH ( ctx - > dstport_str )
# ifdef HAVE_LOCAL_PROCINFO
, lpi
# endif /* HAVE_LOCAL_PROCINFO */
# ifndef WITHOUT_USERAUTH
, STRORDASH ( ctx - > user )
# endif /* !WITHOUT_USERAUTH */
) ;
} else {
rv = asprintf ( & msg , " CONN: %s %s %s %s %s "
" sni:%s names:%s "
" sproto:%s:%s dproto:%s:%s "
" origcrt:%s usedcrt:%s "
# ifdef HAVE_LOCAL_PROCINFO
" %s "
# endif /* HAVE_LOCAL_PROCINFO */
# ifndef WITHOUT_USERAUTH
" user:%s "
# endif /* !WITHOUT_USERAUTH */
" \n " ,
ctx - > proto = = PROTO_AUTOSSL ? " autossl " : ( ctx - > proto = = PROTO_POP3S ? " pop3s " : ( ctx - > proto = = PROTO_SMTPS ? " smtps " : " ssl " ) ) ,
STRORDASH ( ctx - > srchost_str ) ,
STRORDASH ( ctx - > srcport_str ) ,
STRORDASH ( ctx - > dsthost_str ) ,
STRORDASH ( ctx - > dstport_str ) ,
STRORDASH ( ctx - > sslctx - > sni ) ,
STRORDASH ( ctx - > sslctx - > ssl_names ) ,
SSL_get_version ( ctx - > src . ssl ) ,
SSL_get_cipher ( ctx - > src . ssl ) ,
STRORDASH ( ctx - > sslctx - > srvdst_ssl_version ) ,
STRORDASH ( ctx - > sslctx - > srvdst_ssl_cipher ) ,
STRORDASH ( ctx - > sslctx - > origcrtfpr ) ,
STRORDASH ( ctx - > sslctx - > usedcrtfpr )
# ifdef HAVE_LOCAL_PROCINFO
, lpi
# endif /* HAVE_LOCAL_PROCINFO */
# ifndef WITHOUT_USERAUTH
, STRORDASH ( ctx - > user )
# endif /* !WITHOUT_USERAUTH */
) ;
}
if ( ( rv < 0 ) | | ! msg ) {
ctx - > enomem = 1 ;
goto out ;
}
if ( ! ctx - > global - > detach ) {
log_err_printf ( " %s " , msg ) ;
} else if ( ctx - > global - > statslog ) {
if ( log_conn ( msg ) = = - 1 ) {
log_err_level_printf ( LOG_WARNING , " Conn logging failed \n " ) ;
}
}
if ( ctx - > global - > connectlog ) {
if ( log_connect_print_free ( msg ) = = - 1 ) {
log_err_level_printf ( LOG_WARNING , " Connection logging failed \n " ) ;
}
} else {
free ( msg ) ;
}
out :
# ifdef HAVE_LOCAL_PROCINFO
if ( lpi & & ctx - > global - > lprocinfo ) {
free ( lpi ) ;
}
# endif /* HAVE_LOCAL_PROCINFO */
return ;
}
static int NONNULL ( 1 )
pxy_log_content_inbuf ( pxy_conn_ctx_t * ctx , struct evbuffer * inbuf , int req )
{
if ( ! ctx - > log_content & & ! ctx - > log_pcap
# ifndef WITHOUT_MIRROR
& & ! ctx - > log_mirror
# endif /* !WITHOUT_MIRROR */
) {
return 0 ;
}
size_t sz = evbuffer_get_length ( inbuf ) ;
unsigned char * buf = malloc ( sz ) ;
if ( ! buf ) {
ctx - > enomem = 1 ;
return - 1 ;
}
if ( evbuffer_copyout ( inbuf , buf , sz ) = = - 1 ) {
free ( buf ) ;
return - 1 ;
}
logbuf_t * lb = logbuf_new_alloc ( sz , NULL ) ;
if ( ! lb ) {
free ( buf ) ;
ctx - > enomem = 1 ;
return - 1 ;
}
memcpy ( lb - > buf , buf , lb - > sz ) ;
free ( buf ) ;
if ( log_content_submit ( & ctx - > logctx , lb , req , ctx - > log_content , ctx - > log_pcap
# ifndef WITHOUT_MIRROR
, ctx - > log_mirror
# endif /* !WITHOUT_MIRROR */
) = = - 1 ) {
logbuf_free ( lb ) ;
log_err_level_printf ( LOG_WARNING , " Content log submission failed \n " ) ;
return - 1 ;
}
return 0 ;
}
# ifdef HAVE_LOCAL_PROCINFO
int
pxy_prepare_logging_local_procinfo ( pxy_conn_ctx_t * ctx )
{
if ( ctx - > global - > lprocinfo ) {
/* fetch process info */
if ( proc_pid_for_addr ( & ctx - > lproc . pid ,
( struct sockaddr * ) & ctx - > lproc . srcaddr ,
ctx - > lproc . srcaddrlen ) = = 0 & &
ctx - > lproc . pid ! = - 1 & &
proc_get_info ( ctx - > lproc . pid ,
& ctx - > lproc . exec_path ,
& ctx - > lproc . uid ,
& ctx - > lproc . gid ) = = 0 ) {
/* fetch user/group names */
ctx - > lproc . user = sys_user_str (
ctx - > lproc . uid ) ;
ctx - > lproc . group = sys_group_str (
ctx - > lproc . gid ) ;
if ( ! ctx - > lproc . user | |
! ctx - > lproc . group ) {
ctx - > enomem = 1 ;
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
}
}
return 0 ;
}
# endif /* HAVE_LOCAL_PROCINFO */
static int
pxy_prepare_logging ( pxy_conn_ctx_t * ctx )
{
/* prepare logging, part 2 */
# ifdef HAVE_LOCAL_PROCINFO
if ( WANT_CONNECT_LOG ( ctx ) | | WANT_CONTENT_LOG ( ctx ) ) {
if ( pxy_prepare_logging_local_procinfo ( ctx ) = = - 1 ) {
return - 1 ;
}
}
# endif /* HAVE_LOCAL_PROCINFO */
if ( WANT_CONTENT_LOG ( ctx ) ) {
if ( log_content_open ( & ctx - > logctx , ctx - > global ,
( struct sockaddr * ) & ctx - > srcaddr ,
ctx - > srcaddrlen ,
( struct sockaddr * ) & ctx - > dstaddr ,
ctx - > dstaddrlen ,
STRORDASH ( ctx - > srchost_str ) , STRORDASH ( ctx - > srcport_str ) ,
STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) ,
# ifdef HAVE_LOCAL_PROCINFO
ctx - > lproc . exec_path ,
ctx - > lproc . user ,
ctx - > lproc . group ,
# else /* HAVE_LOCAL_PROCINFO */
NULL , NULL , NULL ,
# endif /* HAVE_LOCAL_PROCINFO */
ctx - > log_content , ctx - > log_pcap
# ifndef WITHOUT_MIRROR
, ctx - > log_mirror
# endif /* !WITHOUT_MIRROR */
) = = - 1 ) {
if ( errno = = ENOMEM )
ctx - > enomem = 1 ;
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
}
return 0 ;
}
static void NONNULL ( 1 , 2 )
pxy_log_dbg_connect_type ( pxy_conn_ctx_t * ctx , pxy_conn_desc_t * this )
{
if ( OPTS_DEBUG ( ctx - > global ) ) {
if ( this - > ssl ) {
char * keystr ;
/* for SSL, we get two connect events */
log_dbg_printf ( " %s connected to [%s]:%s %s %s \n " ,
protocol_names [ ctx - > proto ] ,
STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) ,
SSL_get_version ( this - > ssl ) , SSL_get_cipher ( this - > ssl ) ) ;
keystr = ssl_ssl_masterkey_to_str ( this - > ssl ) ;
if ( keystr ) {
log_dbg_print_free ( keystr ) ;
}
} else {
/* for TCP, we get only a dst connect event,
* since src was already connected from the
* beginning ; mirror SSL debug output anyway
* in order not to confuse anyone who might be
* looking closely at the output */
log_dbg_printf ( " %s connected to [%s]:%s \n " ,
protocol_names [ ctx - > proto ] ,
STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) ) ;
log_dbg_printf ( " %s connected from [%s]:%s \n " ,
protocol_names [ ctx - > proto ] ,
STRORDASH ( ctx - > srchost_str ) , STRORDASH ( ctx - > srcport_str ) ) ;
}
}
}
void
pxy_log_connect_src ( pxy_conn_ctx_t * ctx )
{
/* log connection if we don't analyze any headers */
if ( ! ctx - > spec - > http & & WANT_CONNECT_LOG ( ctx ) ) {
pxy_log_connect_nonhttp ( ctx ) ;
}
if ( ctx - > src . ssl & & ctx - > log_cert & & ctx - > global - > certgendir ) {
/* write SSL certificates to gendir */
protossl_srccert_write ( ctx ) ;
}
if ( protossl_log_masterkey ( ctx , & ctx - > src ) = = - 1 ) {
return ;
}
pxy_log_dbg_connect_type ( ctx , & ctx - > src ) ;
}
void
pxy_log_connect_srvdst ( pxy_conn_ctx_t * ctx )
{
// @attention srvdst.bev may be NULL, if its writecb fires first
if ( ctx - > srvdst . bev ) {
/* log connection if we don't analyze any headers */
if ( ! ctx - > srvdst . ssl & & ! ctx - > spec - > http & & WANT_CONNECT_LOG ( ctx ) ) {
pxy_log_connect_nonhttp ( ctx ) ;
}
if ( protossl_log_masterkey ( ctx , & ctx - > srvdst ) = = - 1 ) {
return ;
}
pxy_log_dbg_connect_type ( ctx , & ctx - > srvdst ) ;
}
}
static void
pxy_log_dbg_disconnect ( pxy_conn_ctx_t * ctx )
{
/* we only get a single disconnect event here for both connections */
if ( OPTS_DEBUG ( ctx - > global ) ) {
log_dbg_printf ( " %s disconnected to [%s]:%s, fd=%d \n " ,
protocol_names [ ctx - > proto ] ,
STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) , ctx - > fd ) ;
log_dbg_printf ( " %s disconnected from [%s]:%s, fd=%d \n " ,
protocol_names [ ctx - > proto ] ,
STRORDASH ( ctx - > srchost_str ) , STRORDASH ( ctx - > srcport_str ) , ctx - > fd ) ;
}
}
static void
pxy_log_dbg_disconnect_child ( pxy_conn_child_ctx_t * ctx )
{
/* we only get a single disconnect event here for both connections */
if ( OPTS_DEBUG ( ctx - > conn - > global ) ) {
log_dbg_printf ( " Child %s disconnected to [%s]:%s, child fd=%d, fd=%d \n " ,
protocol_names [ ctx - > conn - > proto ] ,
STRORDASH ( ctx - > conn - > dsthost_str ) , STRORDASH ( ctx - > conn - > dstport_str ) , ctx - > fd , ctx - > conn - > fd ) ;
log_dbg_printf ( " Child %s disconnected from [%s]:%s, child fd=%d, fd=%d \n " ,
protocol_names [ ctx - > conn - > proto ] ,
STRORDASH ( ctx - > conn - > srchost_str ) , STRORDASH ( ctx - > conn - > srcport_str ) , ctx - > fd , ctx - > conn - > fd ) ;
}
}
unsigned char *
pxy_malloc_packet ( size_t sz , pxy_conn_ctx_t * ctx )
{
unsigned char * packet = malloc ( sz ) ;
if ( ! packet ) {
ctx - > enomem = 1 ;
return NULL ;
}
return packet ;
}
# ifdef DEBUG_PROXY
static void
pxy_insert_sslproxy_header ( pxy_conn_ctx_t * ctx , unsigned char * packet , size_t * packet_size )
{
log_finer ( " ENTER " ) ;
// @attention Cannot use string manipulation functions; we are dealing with binary arrays here, not NULL-terminated strings
memmove ( packet + ctx - > sslproxy_header_len + 2 , packet , * packet_size ) ;
memcpy ( packet , ctx - > sslproxy_header , ctx - > sslproxy_header_len ) ;
memcpy ( packet + ctx - > sslproxy_header_len , " \r \n " , 2 ) ;
* packet_size + = ctx - > sslproxy_header_len + 2 ;
ctx - > sent_sslproxy_header = 1 ;
}
# endif /* DEBUG_PROXY */
int
pxy_try_prepend_sslproxy_header ( pxy_conn_ctx_t * ctx , struct evbuffer * inbuf , struct evbuffer * outbuf )
{
log_finer ( " ENTER " ) ;
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
if ( ctx - > divert & & ! ctx - > sent_sslproxy_header ) {
# ifdef DEBUG_PROXY
size_t packet_size = evbuffer_get_length ( inbuf ) ;
// +2 for \r\n
unsigned char * packet = pxy_malloc_packet ( packet_size + ctx - > sslproxy_header_len + 2 , ctx ) ;
if ( ! packet ) {
return - 1 ;
}
evbuffer_remove ( inbuf , packet , packet_size ) ;
log_finest_va ( " ORIG packet, size=%zu: \n %.*s " , packet_size , ( int ) packet_size , packet ) ;
pxy_insert_sslproxy_header ( ctx , packet , & packet_size ) ;
evbuffer_add ( outbuf , packet , packet_size ) ;
log_finest_va ( " NEW packet, size=%zu: \n %.*s " , packet_size , ( int ) packet_size , packet ) ;
free ( packet ) ;
}
else {
evbuffer_add_buffer ( outbuf , inbuf ) ;
}
# else /* DEBUG_PROXY */
evbuffer_add_printf ( outbuf , " %s \r \n " , ctx - > sslproxy_header ) ;
ctx - > sent_sslproxy_header = 1 ;
}
evbuffer_add_buffer ( outbuf , inbuf ) ;
# endif /* !DEBUG_PROXY */
return 0 ;
}
void
pxy_try_remove_sslproxy_header ( pxy_conn_child_ctx_t * ctx , unsigned char * packet , size_t * packet_size )
{
// @attention Cannot use string manipulation functions; we are dealing with binary arrays here, not NULL-terminated strings
unsigned char * pos = memmem ( packet , * packet_size , ctx - > conn - > sslproxy_header , ctx - > conn - > sslproxy_header_len ) ;
if ( pos ) {
log_finer ( " REMOVE " ) ;
memmove ( pos , pos + ctx - > conn - > sslproxy_header_len + 2 , * packet_size - ( pos - packet ) - ( ctx - > conn - > sslproxy_header_len + 2 ) ) ;
* packet_size - = ctx - > conn - > sslproxy_header_len + 2 ;
ctx - > removed_sslproxy_header = 1 ;
}
}
# if defined(__APPLE__) || defined(__FreeBSD__)
# define getdtablecount() 0
/*
* Copied from :
* opensmtpd - 201801101641 p1 / openbsd - compat / imsg . c
*
* Copyright ( c ) 2003 , 2004 Henning Brauer < henning @ openbsd . org >
*
* Permission to use , copy , modify , and distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
static int
available_fds ( unsigned int n )
{
unsigned int i ;
int ret , fds [ 256 ] ;
if ( n > ( sizeof ( fds ) / sizeof ( fds [ 0 ] ) ) )
return - 1 ;
ret = 0 ;
for ( i = 0 ; i < n ; i + + ) {
fds [ i ] = - 1 ;
if ( ( fds [ i ] = socket ( AF_INET , SOCK_DGRAM , 0 ) ) < 0 ) {
ret = - 1 ;
break ;
}
}
for ( i = 0 ; i < n & & fds [ i ] > = 0 ; i + + )
close ( fds [ i ] ) ;
return ret ;
}
# endif /* __APPLE__ || __FreeBSD__ */
# ifdef __linux__
/*
* Copied from :
* https : //github.com/tmux/tmux/blob/master/compat/getdtablecount.c
*
* Copyright ( c ) 2017 Nicholas Marriott < nicholas . marriott @ gmail . com >
*
* Permission to use , copy , modify , and distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND , USE , DATA OR PROFITS , WHETHER
* IN AN ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
int
getdtablecount ( )
{
char path [ PATH_MAX ] ;
glob_t g ;
int n = 0 ;
if ( snprintf ( path , sizeof path , " /proc/%ld/fd/* " , ( long ) getpid ( ) ) < 0 ) {
log_err_level_printf ( LOG_CRIT , " snprintf overflow \n " ) ;
return 0 ;
}
if ( glob ( path , 0 , NULL , & g ) = = 0 )
n = g . gl_pathc ;
globfree ( & g ) ;
return n ;
}
# endif /* __linux__ */
/*
* Check if we are out of file descriptors to close the conn , or else libevent will crash us
* @ attention We cannot guess the number of children in a connection at conn setup time . So , FD_RESERVE is just a ball park figure .
* But what if a connection passes the check below , but eventually tries to create more children than FD_RESERVE allows for ? This will crash us the same .
* Beware , this applies to all current conns , not just the last connection setup .
* For example , 20 x conns pass the check below before creating any children , at which point we reach the last FD_RESERVE fds ,
* then they all start creating children , which crashes us again .
* So , no matter how large an FD_RESERVE we choose , there will always be a risk of running out of fds , if we check the number of fds during parent conn setup only .
* If we are left with less than FD_RESERVE fds , we should not create more children than FD_RESERVE allows for either .
* Therefore , we check if we are out of fds in pxy_listener_acceptcb_child ( ) and close the conn there too .
* @ attention These checks are expected to slow us further down , but it is critical to avoid a crash in case we run out of fds .
*/
static int
check_fd_usage (
# ifdef DEBUG_PROXY
pxy_conn_ctx_t * ctx
# endif /* DEBUG_PROXY */
)
{
int dtable_count = getdtablecount ( ) ;
log_finer_va ( " descriptor_table_size=%d, dtablecount=%d, reserve=%d " , descriptor_table_size , dtable_count , FD_RESERVE ) ;
if ( dtable_count + FD_RESERVE > = descriptor_table_size ) {
goto out ;
}
# if defined(__APPLE__) || defined(__FreeBSD__)
if ( available_fds ( FD_RESERVE ) = = - 1 ) {
goto out ;
}
# endif /* __APPLE__ || __FreeBSD__ */
return 0 ;
out :
errno = EMFILE ;
log_err_level_printf ( LOG_CRIT , " Out of file descriptors \n " ) ;
return - 1 ;
}
/*
* Callback for accept events on the socket listener bufferevent .
*/
static void
pxy_listener_acceptcb_child ( UNUSED struct evconnlistener * listener , evutil_socket_t fd ,
UNUSED struct sockaddr * peeraddr , UNUSED int peeraddrlen , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
ctx - > atime = time ( NULL ) ;
# ifdef DEBUG_PROXY
log_finest_va ( " ENTER, fd=%d, ctx->child_fd=%d " , fd , ctx - > child_fd ) ;
char * host , * port ;
if ( sys_sockaddr_str ( peeraddr , peeraddrlen , & host , & port ) = = 0 ) {
log_finest_va ( " peer addr=[%s]:%s, fd=%d " , host , port , fd ) ;
free ( host ) ;
free ( port ) ;
}
# endif /* DEBUG_PROXY */
if ( ! ctx - > dstaddrlen ) {
log_err_level_printf ( LOG_CRIT , " Child no target address; aborting connection \n " ) ;
evutil_closesocket ( fd ) ;
pxy_conn_term ( ctx , 1 ) ;
goto out ;
}
if ( check_fd_usage (
# ifdef DEBUG_PROXY
ctx
# endif /* DEBUG_PROXY */
) = = - 1 ) {
evutil_closesocket ( fd ) ;
pxy_conn_term ( ctx , 1 ) ;
goto out ;
}
pxy_conn_child_ctx_t * child_ctx = pxy_conn_ctx_new_child ( fd , ctx ) ;
if ( ! child_ctx ) {
log_err_level_printf ( LOG_CRIT , " Error allocating memory \n " ) ;
evutil_closesocket ( fd ) ;
pxy_conn_term ( ctx , 1 ) ;
goto out ;
}
pxy_conn_attach_child ( child_ctx ) ;
// @attention Do not enable src events here yet, they will be enabled after dst connects
if ( prototcp_setup_src_child ( child_ctx ) = = - 1 ) {
goto out ;
}
// @attention fd (child_ctx->fd) is different from child event listener fd (ctx->child_fd)
ctx - > thr - > max_fd = max ( ctx - > thr - > max_fd , child_ctx - > fd ) ;
ctx - > child_src_fd = child_ctx - > fd ;
/* create server-side socket and eventbuffer */
// Children rely on the findings of parent
int connect_retval = child_ctx - > protoctx - > connectcb ( child_ctx ) ;
if ( connect_retval = = - 1 | | ctx - > term | | ctx - > enomem ) {
goto out ;
}
if ( OPTS_DEBUG ( ctx - > global ) ) {
log_dbg_printf ( " Child connecting to [%s]:%s \n " , STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) ) ;
}
// initiate connection, except for the first child conn which uses the parent's srvdst as dst
// connectcb returns 1 if we have reused srvdst as the dst of the first child conn, and 0 for the other child conns
if ( connect_retval = = 0 ) {
if ( bufferevent_socket_connect ( child_ctx - > dst . bev , ( struct sockaddr * ) & ctx - > dstaddr , ctx - > dstaddrlen ) = = - 1 ) {
pxy_conn_term ( ctx , 1 ) ;
goto out ;
}
}
child_ctx - > dst_fd = bufferevent_getfd ( child_ctx - > dst . bev ) ;
ctx - > child_dst_fd = child_ctx - > dst_fd ;
ctx - > thr - > max_fd = max ( ctx - > thr - > max_fd , child_ctx - > dst_fd ) ;
// Do not return here, but continue and check term/enomem flags below
out :
// @attention Do not use child_ctx->conn here, child_ctx may be uninitialized
// @attention Call pxy_conn_free() directly, not pxy_conn_term() here
// This is our last chance to close and free the conn
if ( ctx - > term | | ctx - > enomem ) {
pxy_conn_free ( ctx , ctx - > term ? ctx - > term_requestor : 1 ) ;
}
}
static int WUNRES NONNULL ( 1 )
pxy_opensock_child ( pxy_conn_ctx_t * ctx )
{
evutil_socket_t fd = socket ( ctx - > spec - > return_addr . ss_family , SOCK_STREAM , IPPROTO_TCP ) ;
if ( fd = = - 1 ) {
log_err_level_printf ( LOG_CRIT , " Error from socket(): %s (%i) \n " , strerror ( errno ) , errno ) ;
log_fine_va ( " Error from socket(): %s (%i) " , strerror ( errno ) , errno ) ;
evutil_closesocket ( fd ) ;
return - 1 ;
}
if ( evutil_make_socket_nonblocking ( fd ) = = - 1 ) {
log_err_level_printf ( LOG_CRIT , " Error making socket nonblocking: %s (%i) \n " , strerror ( errno ) , errno ) ;
log_fine_va ( " Error making socket nonblocking: %s (%i) " , strerror ( errno ) , errno ) ;
evutil_closesocket ( fd ) ;
return - 1 ;
}
int on = 1 ;
if ( setsockopt ( fd , SOL_SOCKET , SO_KEEPALIVE , ( void * ) & on , sizeof ( on ) ) = = - 1 ) {
log_err_level_printf ( LOG_CRIT , " Error from setsockopt(SO_KEEPALIVE): %s (%i) \n " , strerror ( errno ) , errno ) ;
log_fine_va ( " Error from setsockopt(SO_KEEPALIVE): %s (%i) " , strerror ( errno ) , errno ) ;
evutil_closesocket ( fd ) ;
return - 1 ;
}
if ( evutil_make_listen_socket_reuseable ( fd ) = = - 1 ) {
log_err_level_printf ( LOG_CRIT , " Error from setsockopt(SO_REUSABLE): %s (%i) \n " , strerror ( errno ) , errno ) ;
log_fine_va ( " Error from setsockopt(SO_REUSABLE): %s (%i) " , strerror ( errno ) , errno ) ;
evutil_closesocket ( fd ) ;
return - 1 ;
}
if ( bind ( fd , ( struct sockaddr * ) & ctx - > spec - > return_addr , ctx - > spec - > return_addrlen ) = = - 1 ) {
log_err_level_printf ( LOG_CRIT , " Error from bind(): %s (%i) \n " , strerror ( errno ) , errno ) ;
log_fine_va ( " Error from bind(): %s (%i) " , strerror ( errno ) , errno ) ;
evutil_closesocket ( fd ) ;
return - 1 ;
}
return fd ;
}
int
pxy_set_sslproxy_header ( pxy_conn_ctx_t * ctx , int upgraded )
{
struct sockaddr_in child_listener_addr ;
socklen_t child_listener_len = sizeof ( child_listener_addr ) ;
if ( getsockname ( ctx - > child_fd , ( struct sockaddr * ) & child_listener_addr , & child_listener_len ) < 0 ) {
log_err_level_printf ( LOG_CRIT , " Error in getsockname: %s \n " , strerror ( errno ) ) ;
// @attention Do not close the child fd here, because child evcl exists now, hence pxy_conn_free() will close it while freeing child_evcl
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
// @todo Children are assumed to be listening on an IPv4 address, should we support IPv6 children?
char addr [ INET_ADDRSTRLEN ] ;
if ( ! inet_ntop ( AF_INET , & child_listener_addr . sin_addr , addr , INET_ADDRSTRLEN ) ) {
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
// Port may be 4 or 5 chars long
unsigned int port = ntohs ( child_listener_addr . sin_port ) ;
size_t port_len = port < 10000 ? 4 : 5 ;
# ifndef WITHOUT_USERAUTH
int user_len = 0 ;
if ( ctx - > conn_opts - > user_auth & & ctx - > user ) {
// +1 for comma
user_len = strlen ( ctx - > user ) + 1 ;
}
# endif /* !WITHOUT_USERAUTH */
// SSLproxy: [127.0.0.1]:34649,[192.168.3.24]:47286,[74.125.206.108]:465,s,soner
// SSLproxy: + + [ + addr + ] + : + p + , + [ + srchost_str + ] + : + srcport_str + , + [ + dsthost_str + ] + : + dstport_str + , + s + , + user
// SSLPROXY_KEY_LEN + 1 + 1 + strlen(addr) + 1 + 1 + port_len + 1 + 1 + strlen(ctx->srchost_str) + 1 + 1 + strlen(ctx->srcport_str) + 1 + 1 + strlen(ctx->dsthost_str) + 1 + 1 + strlen(ctx->dstport_str) + 1 + 1 + user_len
ctx - > sslproxy_header_len = SSLPROXY_KEY_LEN + strlen ( addr ) + port_len + strlen ( ctx - > srchost_str ) + strlen ( ctx - > srcport_str ) + strlen ( ctx - > dsthost_str ) + strlen ( ctx - > dstport_str ) + 14
# ifndef WITHOUT_USERAUTH
+ user_len
# endif /* !WITHOUT_USERAUTH */
;
// +1 for NULL
ctx - > sslproxy_header = malloc ( ctx - > sslproxy_header_len + 1 ) ;
if ( ! ctx - > sslproxy_header ) {
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
// printf(3): "snprintf() will write at most size-1 of the characters (the size'th character then gets the terminating NULL)"
// So, +1 for NULL
if ( snprintf ( ctx - > sslproxy_header , ctx - > sslproxy_header_len + 1 , " %s [%s]:%u,[%s]:%s,[%s]:%s,%s "
# ifndef WITHOUT_USERAUTH
" %s%s "
# endif /* !WITHOUT_USERAUTH */
,
SSLPROXY_KEY , addr , port , STRORNONE ( ctx - > srchost_str ) , STRORNONE ( ctx - > srcport_str ) ,
STRORNONE ( ctx - > dsthost_str ) , STRORNONE ( ctx - > dstport_str ) , ctx - > spec - > ssl | | upgraded ? " s " : " p "
# ifndef WITHOUT_USERAUTH
, user_len ? " , " : " " , user_len ? ctx - > user : " "
# endif /* !WITHOUT_USERAUTH */
) < 0 ) {
// ctx->sslproxy_header is freed by pxy_conn_ctx_free()
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
log_finer_va ( " sslproxy_header= %s " , ctx - > sslproxy_header ) ;
return 0 ;
}
int
pxy_setup_child_listener ( pxy_conn_ctx_t * ctx )
{
if ( ! ctx - > divert ) {
// split mode
return 0 ;
}
// @attention Defer child setup and evcl creation until after parent init is complete, otherwise (1) causes multithreading issues (proxy_listener_acceptcb is
// running on a different thread from the conn, and we only have thrmgr mutex), and (2) we need to clean up less upon errors.
// Child evcls use the evbase of the parent thread, otherwise we would get multithreading issues.
// We don't need a privsep call to open a socket for child listener,
// because listener port of child conns are assigned by the system, hence are from non-privileged range above 1024
ctx - > child_fd = pxy_opensock_child ( ctx ) ;
if ( ctx - > child_fd < 0 ) {
log_err_level_printf ( LOG_CRIT , " Error opening child socket: %s (%i) \n " , strerror ( errno ) , errno ) ;
log_fine_va ( " Error opening child socket: %s (%i) " , strerror ( errno ) , errno ) ;
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
ctx - > thr - > max_fd = max ( ctx - > thr - > max_fd , ctx - > child_fd ) ;
// @attention Do not pass NULL as user-supplied pointer
struct evconnlistener * child_evcl = evconnlistener_new ( ctx - > thr - > evbase , pxy_listener_acceptcb_child , ctx , LEV_OPT_CLOSE_ON_FREE , 1024 , ctx - > child_fd ) ;
if ( ! child_evcl ) {
log_err_level_printf ( LOG_CRIT , " Error creating child evconnlistener: %s \n " , strerror ( errno ) ) ;
log_fine_va ( " Error creating child evconnlistener: %s " , strerror ( errno ) ) ;
// @attention Close child fd separately, because child evcl does not exist yet, hence fd would not be closed by calling pxy_conn_free()
evutil_closesocket ( ctx - > child_fd ) ;
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
ctx - > child_evcl = child_evcl ;
evconnlistener_set_error_cb ( child_evcl , proxy_listener_errorcb ) ;
log_finer_va ( " Finished setting up child listener, child_fd=%d " , ctx - > child_fd ) ;
return pxy_set_sslproxy_header ( ctx , 0 ) ;
}
int
pxy_try_close_conn_end ( pxy_conn_desc_t * conn_end , pxy_conn_ctx_t * ctx )
{
/* if the other end is still open and doesn't have data
* to send , close it , otherwise its writecb will close
* it after writing what ' s left in the output buffer */
if ( ! ctx - > protoctx - > outbuf_has_datacb ( conn_end - > bev
# ifdef DEBUG_PROXY
, " conn " , ctx
# endif /* DEBUG_PROXY */
) ) {
log_finest ( " outbuflen == 0, terminate conn " ) ;
conn_end - > free ( conn_end - > bev , ctx ) ;
conn_end - > bev = NULL ;
conn_end - > closed = 1 ;
return 1 ;
}
return 0 ;
}
void
pxy_try_disconnect ( pxy_conn_ctx_t * ctx , pxy_conn_desc_t * this , pxy_conn_desc_t * other , int is_requestor )
{
this - > closed = 1 ;
this - > free ( this - > bev , ctx ) ;
this - > bev = NULL ;
if ( other - > closed ) {
log_finest ( " other->closed, terminate conn " ) ;
// Uses only ctx to log disconnect, never any of the bevs
pxy_log_dbg_disconnect ( ctx ) ;
pxy_conn_term ( ctx , is_requestor ) ;
}
}
void
pxy_try_disconnect_child ( pxy_conn_child_ctx_t * ctx , pxy_conn_desc_t * this , pxy_conn_desc_t * other )
{
this - > closed = 1 ;
this - > free ( this - > bev , ctx - > conn ) ;
this - > bev = NULL ;
if ( other - > closed ) {
log_finest ( " other->closed, terminate conn " ) ;
// Uses only ctx to log disconnect, never any of the bevs
pxy_log_dbg_disconnect_child ( ctx ) ;
pxy_conn_term_child ( ctx ) ;
}
}
int
pxy_try_consume_last_input ( struct bufferevent * bev , pxy_conn_ctx_t * ctx )
{
/* if there is data pending in the closed connection,
* handle it here , otherwise it will be lost . */
if ( evbuffer_get_length ( bufferevent_get_input ( bev ) ) ) {
log_fine ( " evbuffer_get_length(inbuf) > 0, terminate conn " ) ;
if ( pxy_bev_readcb_preexec_logging_and_stats ( bev , ctx ) = = - 1 ) {
return - 1 ;
}
ctx - > protoctx - > bev_readcb ( bev , ctx ) ;
}
return 0 ;
}
int
pxy_try_consume_last_input_child ( struct bufferevent * bev , pxy_conn_child_ctx_t * ctx )
{
/* if there is data pending in the closed connection,
* handle it here , otherwise it will be lost . */
if ( evbuffer_get_length ( bufferevent_get_input ( bev ) ) ) {
log_fine ( " evbuffer_get_length(inbuf) > 0, terminate conn " ) ;
if ( pxy_bev_readcb_preexec_logging_and_stats_child ( bev , ctx ) = = - 1 ) {
return - 1 ;
}
ctx - > protoctx - > bev_readcb ( bev , ctx ) ;
}
return 0 ;
}
Add support for passsite substring match
Now the site field in PassSite option can have an '*' suffix to search
for a match anywhere in sni or common names. Note that this is not a
regex or wildcard search.
Previously, we only supported exact matches in sni and between slashes
in common names. This change makes it possible to cover multiple sites
in one PassSite option. In fact, without this change, certain sites
could not be added as passsite, because it was impossible to know their
subdomain names beforehand, for example *.fbcdn.net, which may have many
subdomain names in place of asterisk.
So to use substring match, append an '*' to a site name in PassSite
option (the asterisk is removed before substring search). For example,
use ".fbcdn.net*" to match all subdomains of fbcdn.net, notice the
asterisk at the end.
We also add a warning log starting with "Closing on ssl error without
passsite match" to report sites that can be added as passsite, which is
expected to help in writing PassSite rules.
Also, we now set dstaddr_str earlier in conn handling, so we can print
it in debug logs. This also helps in IDLE and EXPIRED conn logs.
3 years ago
static int NONNULL ( 1 )
pxy_set_dstaddr ( pxy_conn_ctx_t * ctx )
{
if ( sys_sockaddr_str ( ( struct sockaddr * ) & ctx - > dstaddr , ctx - > dstaddrlen , & ctx - > dsthost_str , & ctx - > dstport_str ) ! = 0 ) {
// sys_sockaddr_str() may fail due to either malloc() or getnameinfo()
ctx - > enomem = 1 ;
pxy_conn_term ( ctx , 1 ) ;
return - 1 ;
}
return 0 ;
}
int
pxy_bev_readcb_preexec_logging_and_stats ( struct bufferevent * bev , pxy_conn_ctx_t * ctx )
{
if ( bev = = ctx - > src . bev | | bev = = ctx - > dst . bev ) {
struct evbuffer * inbuf = bufferevent_get_input ( bev ) ;
size_t inbuf_size = evbuffer_get_length ( inbuf ) ;
if ( bev = = ctx - > src . bev ) {
ctx - > thr - > intif_in_bytes + = inbuf_size ;
} else {
ctx - > thr - > intif_out_bytes + = inbuf_size ;
}
if ( WANT_CONTENT_LOG ( ctx - > conn ) ) {
// HTTP content logging at this point may record certain header lines twice, if we have not seen all headers yet
return pxy_log_content_inbuf ( ctx , inbuf , ( bev = = ctx - > src . bev ) ) ;
}
}
return 0 ;
}
/*
* Callback for read events on the up - and downstream connection bufferevents .
* Called when there is data ready in the input evbuffer .
*/
void
pxy_bev_readcb ( struct bufferevent * bev , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
if ( pxy_bev_readcb_preexec_logging_and_stats ( bev , ctx ) = = - 1 ) {
goto out ;
}
if ( ! ctx - > connected ) {
log_err_level ( LOG_CRIT , " readcb called when not connected - aborting " ) ;
log_exceptcb ( ) ;
return ;
}
ctx - > atime = time ( NULL ) ;
ctx - > protoctx - > bev_readcb ( bev , ctx ) ;
out :
if ( ctx - > term | | ctx - > enomem ) {
pxy_conn_free ( ctx , ctx - > term ? ctx - > term_requestor : ( bev = = ctx - > src . bev ) ) ;
}
}
int
pxy_bev_readcb_preexec_logging_and_stats_child ( struct bufferevent * bev , pxy_conn_child_ctx_t * ctx )
{
struct evbuffer * inbuf = bufferevent_get_input ( bev ) ;
size_t inbuf_size = evbuffer_get_length ( inbuf ) ;
if ( bev = = ctx - > src . bev ) {
ctx - > conn - > thr - > extif_out_bytes + = inbuf_size ;
} else {
ctx - > conn - > thr - > extif_in_bytes + = inbuf_size ;
}
if ( WANT_CONTENT_LOG ( ctx - > conn ) ) {
return pxy_log_content_inbuf ( ctx - > conn , inbuf , ( bev = = ctx - > src . bev ) ) ;
}
return 0 ;
}
void
pxy_bev_readcb_child ( struct bufferevent * bev , void * arg )
{
pxy_conn_child_ctx_t * ctx = arg ;
if ( pxy_bev_readcb_preexec_logging_and_stats_child ( bev , ctx ) = = - 1 ) {
goto out ;
}
if ( ! ctx - > connected ) {
log_err_level ( LOG_CRIT , " readcb called when not connected - aborting " ) ;
log_exceptcb ( ) ;
return ;
}
ctx - > conn - > atime = time ( NULL ) ;
ctx - > protoctx - > bev_readcb ( bev , ctx ) ;
out :
if ( ctx - > conn - > term | | ctx - > conn - > enomem ) {
pxy_conn_free ( ctx - > conn , ctx - > conn - > term ? ctx - > conn - > term_requestor : ( bev = = ctx - > src . bev ) ) ;
return ;
}
if ( ctx - > term ) {
pxy_conn_free_child ( ctx ) ;
}
}
/*
* Callback for write events on the up - and downstream connection bufferevents .
* Called when either all data from the output evbuffer has been written ,
* or if the outbuf is only half full again after having been full .
*/
void
pxy_bev_writecb ( struct bufferevent * bev , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
ctx - > atime = time ( NULL ) ;
ctx - > protoctx - > bev_writecb ( bev , ctx ) ;
if ( ctx - > term | | ctx - > enomem ) {
pxy_conn_free ( ctx , ctx - > term ? ctx - > term_requestor : ( bev = = ctx - > src . bev ) ) ;
}
}
void
pxy_bev_writecb_child ( struct bufferevent * bev , void * arg )
{
pxy_conn_child_ctx_t * ctx = arg ;
ctx - > conn - > atime = time ( NULL ) ;
ctx - > protoctx - > bev_writecb ( bev , ctx ) ;
if ( ctx - > conn - > term | | ctx - > conn - > enomem ) {
pxy_conn_free ( ctx - > conn , ctx - > conn - > term ? ctx - > conn - > term_requestor : ( bev = = ctx - > src . bev ) ) ;
return ;
}
if ( ctx - > term ) {
pxy_conn_free_child ( ctx ) ;
}
}
static int NONNULL ( 1 , 3 )
pxy_bev_eventcb_postexec_logging_and_stats ( struct bufferevent * bev , short events , pxy_conn_ctx_t * ctx )
{
if ( ctx - > term | | ctx - > enomem ) {
return - 1 ;
}
if ( events & BEV_EVENT_CONNECTED ) {
// Passthrough proto does its own connect logging
if ( ctx - > proto ! = PROTO_PASSTHROUGH ) {
if ( bev = = ctx - > src . bev ) {
// @todo When do we reach here? If proto is autossl? Otherwise, src is connected in acceptcb.
pxy_log_connect_src ( ctx ) ;
} else if ( ctx - > connected ) {
if ( pxy_prepare_logging ( ctx ) = = - 1 ) {
return - 1 ;
}
// Doesn't log connect if proto is http, http proto does its own connect logging
pxy_log_connect_srvdst ( ctx ) ;
}
}
if ( bev = = ctx - > srvdst . bev ) {
ctx - > thr - > max_load = max ( ctx - > thr - > max_load , ctx - > thr - > load ) ;
ctx - > thr - > max_fd = max ( ctx - > thr - > max_fd , ctx - > fd ) ;
// src and other fd stats are collected in acceptcb functions
ctx - > srvdst_fd = bufferevent_getfd ( ctx - > srvdst . bev ) ;
ctx - > thr - > max_fd = max ( ctx - > thr - > max_fd , ctx - > srvdst_fd ) ;
// Passthrough proto may have a NULL dst.bev
if ( ctx - > dst . bev ) {
ctx - > dst_fd = bufferevent_getfd ( ctx - > dst . bev ) ;
ctx - > thr - > max_fd = max ( ctx - > thr - > max_fd , ctx - > dst_fd ) ;
}
}
}
return 0 ;
}
/*
* Callback for meta events on the up - and downstream connection bufferevents .
* Called when EOF has been reached , a connection has been made , and on errors .
*/
void
pxy_bev_eventcb ( struct bufferevent * bev , short events , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
ctx - > atime = time ( NULL ) ;
if ( events & BEV_EVENT_ERROR ) {
log_err_printf ( " Client-side BEV_EVENT_ERROR \n " ) ;
ctx - > thr - > errors + + ;
}
ctx - > protoctx - > bev_eventcb ( bev , events , arg ) ;
pxy_bev_eventcb_postexec_logging_and_stats ( bev , events , ctx ) ;
// Logging functions may set term or enomem too
// EOF eventcb may call readcb possibly causing enomem
if ( ctx - > term | | ctx - > enomem ) {
pxy_conn_free ( ctx , ctx - > term ? ctx - > term_requestor : ( bev = = ctx - > src . bev ) ) ;
}
}
void
pxy_bev_eventcb_postexec_stats_child ( short events , pxy_conn_child_ctx_t * ctx )
{
if ( events & BEV_EVENT_CONNECTED ) {
ctx - > conn - > thr - > max_fd = max ( ctx - > conn - > thr - > max_fd , max ( bufferevent_getfd ( ctx - > src . bev ) , bufferevent_getfd ( ctx - > dst . bev ) ) ) ;
}
}
void
pxy_bev_eventcb_child ( struct bufferevent * bev , short events , void * arg )
{
pxy_conn_child_ctx_t * ctx = arg ;
ctx - > conn - > atime = time ( NULL ) ;
if ( events & BEV_EVENT_ERROR ) {
log_err_printf ( " Server-side BEV_EVENT_ERROR \n " ) ;
ctx - > conn - > thr - > errors + + ;
}
// All child conns including this one will be freed if this child engages passthrough mode
// So save the vars used after eventcb call
pxy_conn_ctx_t * conn = ctx - > conn ;
unsigned int term_requestor = bev = = ctx - > src . bev ;
ctx - > protoctx - > bev_eventcb ( bev , events , arg ) ;
// EOF eventcb may call readcb possibly causing enomem
if ( conn - > term | | conn - > enomem ) {
pxy_conn_free ( conn , conn - > term ? conn - > term_requestor : term_requestor ) ;
return ;
}
if ( conn - > children ) {
if ( ctx - > term ) {
pxy_conn_free_child ( ctx ) ;
return ;
}
pxy_bev_eventcb_postexec_stats_child ( events , ctx ) ;
}
}
static filter_action_t * NONNULL ( 1 , 2 )
pxy_conn_filter_match_ip ( pxy_conn_ctx_t * ctx , filter_list_t * list )
{
filter_site_t * site = filter_site_find ( list - > ip_btree , list - > ip_acm , list - > ip_all , ctx - > dsthost_str ) ;
if ( ! site )
return NULL ;
log_fine_va ( " Found site (line=%d): %s for %s:%s, %s:%s " , site - > action . line_num , site - > site ,
STRORDASH ( ctx - > srchost_str ) , STRORDASH ( ctx - > srcport_str ) , STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) ) ;
// Port spec determines the precedence of a site rule, unless the rule does not have any port
if ( ! site - > port_btree & & ! site - > port_acm & & ( site - > action . precedence < ctx - > filter_precedence ) ) {
log_finest_va ( " Rule precedence lower than conn filter precedence %d < %d (line=%d): %s, %s " , site - > action . precedence , ctx - > filter_precedence , site - > action . line_num , site - > site , ctx - > dsthost_str ) ;
return NULL ;
}
# ifdef DEBUG_PROXY
if ( site - > all_sites )
log_finest_va ( " Match all dst (line=%d): %s, %s " , site - > action . line_num , site - > site , ctx - > dsthost_str ) ;
else if ( site - > exact )
log_finest_va ( " Match exact with dst (line=%d): %s, %s " , site - > action . line_num , site - > site , ctx - > dsthost_str ) ;
else
log_finest_va ( " Match substring in dst (line=%d): %s, %s " , site - > action . line_num , site - > site , ctx - > dsthost_str ) ;
# endif /* DEBUG_PROXY */
filter_action_t * port_action = pxy_conn_filter_port ( ctx , site ) ;
if ( port_action )
return port_action ;
return & site - > action ;
}
static filter_action_t * NONNULL ( 1 , 2 )
pxy_conn_dsthost_filter ( pxy_conn_ctx_t * ctx , filter_list_t * list )
{
if ( ctx - > dsthost_str ) {
filter_action_t * action ;
if ( ( action = pxy_conn_filter_match_ip ( ctx , list ) ) )
return pxy_conn_set_filter_action ( action , NULL
# ifdef DEBUG_PROXY
, ctx , ctx - > dsthost_str , NULL
# endif /* DEBUG_PROXY */
) ;
log_finest_va ( " No filter match with ip: %s:%s, %s:%s " ,
STRORDASH ( ctx - > srchost_str ) , STRORDASH ( ctx - > srcport_str ) , STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) ) ;
}
return NULL ;
}
int
pxy_conn_apply_filter ( pxy_conn_ctx_t * ctx , unsigned int defer_action )
{
int rv = 0 ;
filter_action_t * a ;
if ( ( a = pxy_conn_filter ( ctx , pxy_conn_dsthost_filter ) ) ) {
unsigned int action = pxy_conn_translate_filter_action ( ctx , a ) ;
ctx - > filter_precedence = action & FILTER_PRECEDENCE ;
// If we reach here, the matching filtering rule must have a higher precedence
// Override any deferred action, if the current rule action is not match
// Match action cannot override other filter actions
if ( action & FILTER_ACTION_DIVERT ) {
ctx - > deferred_action = FILTER_ACTION_NONE ;
ctx - > divert = 1 ;
}
else if ( action & FILTER_ACTION_SPLIT ) {
ctx - > deferred_action = FILTER_ACTION_NONE ;
ctx - > divert = 0 ;
}
else if ( action & FILTER_ACTION_PASS ) {
if ( defer_action & FILTER_ACTION_PASS ) {
log_fine ( " Deferring pass action " ) ;
ctx - > deferred_action = FILTER_ACTION_PASS ;
}
else {
ctx - > deferred_action = FILTER_ACTION_NONE ;
protopassthrough_engage ( ctx ) ;
ctx - > pass = 1 ;
rv = 1 ;
}
}
else if ( action & FILTER_ACTION_BLOCK ) {
if ( defer_action & FILTER_ACTION_BLOCK ) {
// This block action should override any deferred pass action,
// because the current rule must have a higher precedence
log_fine ( " Deferring block action " ) ;
ctx - > deferred_action = FILTER_ACTION_BLOCK ;
}
else {
pxy_conn_term ( ctx , 1 ) ;
rv = 1 ;
}
}
//else { /* FILTER_ACTION_MATCH */ }
// Filtering rules at higher precedence can enable/disable logging
if ( action & FILTER_LOG_CONNECT )
ctx - > log_connect = 1 ;
else if ( action & FILTER_LOG_NOCONNECT )
ctx - > log_connect = 0 ;
if ( action & FILTER_LOG_MASTER )
ctx - > log_master = 1 ;
else if ( action & FILTER_LOG_NOMASTER )
ctx - > log_master = 0 ;
if ( action & FILTER_LOG_CERT )
ctx - > log_cert = 1 ;
else if ( action & FILTER_LOG_NOCERT )
ctx - > log_cert = 0 ;
if ( action & FILTER_LOG_CONTENT )
ctx - > log_content = 1 ;
else if ( action & FILTER_LOG_NOCONTENT )
ctx - > log_content = 0 ;
if ( action & FILTER_LOG_PCAP )
ctx - > log_pcap = 1 ;
else if ( action & FILTER_LOG_NOPCAP )
ctx - > log_pcap = 0 ;
# ifndef WITHOUT_MIRROR
if ( action & FILTER_LOG_MIRROR )
ctx - > log_mirror = 1 ;
else if ( action & FILTER_LOG_NOMIRROR )
ctx - > log_mirror = 0 ;
# endif /* !WITHOUT_MIRROR */
if ( a - > conn_opts )
ctx - > conn_opts = a - > conn_opts ;
}
return rv ;
}
/*
* Complete the connection . This gets called after finding out where to
* connect to .
*/
void
pxy_conn_connect ( pxy_conn_ctx_t * ctx )
{
log_finest ( " ENTER " ) ;
if ( ! ctx - > dstaddrlen ) {
log_err_level_printf ( LOG_CRIT , " No target address; aborting connection \n " ) ;
evutil_closesocket ( ctx - > fd ) ;
pxy_conn_ctx_free ( ctx , 1 ) ;
return ;
}
Add support for passsite substring match
Now the site field in PassSite option can have an '*' suffix to search
for a match anywhere in sni or common names. Note that this is not a
regex or wildcard search.
Previously, we only supported exact matches in sni and between slashes
in common names. This change makes it possible to cover multiple sites
in one PassSite option. In fact, without this change, certain sites
could not be added as passsite, because it was impossible to know their
subdomain names beforehand, for example *.fbcdn.net, which may have many
subdomain names in place of asterisk.
So to use substring match, append an '*' to a site name in PassSite
option (the asterisk is removed before substring search). For example,
use ".fbcdn.net*" to match all subdomains of fbcdn.net, notice the
asterisk at the end.
We also add a warning log starting with "Closing on ssl error without
passsite match" to report sites that can be added as passsite, which is
expected to help in writing PassSite rules.
Also, we now set dstaddr_str earlier in conn handling, so we can print
it in debug logs. This also helps in IDLE and EXPIRED conn logs.
3 years ago
// This function may be called more than once for the same conn
// So, set the dstaddr only once
if ( ! ctx - > dsthost_str & & ( pxy_set_dstaddr ( ctx ) = = - 1 ) ) {
return ;
}
// Apply dstip filter now, so we can replace the SSL/TLS configuration of the conn with the one in the matching filtering rule
// It does not matter if this function is called more than once for the same conn
// Defer any pass action until srvdst connected
// Defer any block action until HTTP filter application or the first src readcb of non-http proto
if ( pxy_conn_apply_filter ( ctx , FILTER_ACTION_PASS | FILTER_ACTION_BLOCK ) ) {
// We never reach here, since we defer pass and block actions
return ;
}
if ( OPTS_DEBUG ( ctx - > global ) ) {
Add support for passsite substring match
Now the site field in PassSite option can have an '*' suffix to search
for a match anywhere in sni or common names. Note that this is not a
regex or wildcard search.
Previously, we only supported exact matches in sni and between slashes
in common names. This change makes it possible to cover multiple sites
in one PassSite option. In fact, without this change, certain sites
could not be added as passsite, because it was impossible to know their
subdomain names beforehand, for example *.fbcdn.net, which may have many
subdomain names in place of asterisk.
So to use substring match, append an '*' to a site name in PassSite
option (the asterisk is removed before substring search). For example,
use ".fbcdn.net*" to match all subdomains of fbcdn.net, notice the
asterisk at the end.
We also add a warning log starting with "Closing on ssl error without
passsite match" to report sites that can be added as passsite, which is
expected to help in writing PassSite rules.
Also, we now set dstaddr_str earlier in conn handling, so we can print
it in debug logs. This also helps in IDLE and EXPIRED conn logs.
3 years ago
log_dbg_printf ( " Connecting to [%s]:%s \n " , ctx - > dsthost_str , ctx - > dstport_str ) ;
}
int connect_retval = ctx - > protoctx - > connectcb ( ctx ) ;
// The return value of -1 from connectcb indicates that there was a fatal error before event callbacks were set, so we can terminate the connection.
// Otherwise, it is up to the event callbacks to terminate the connection.
if ( connect_retval = = - 1 | | ctx - > term | | ctx - > enomem ) {
pxy_conn_free ( ctx , ctx - > term ? ctx - > term_requestor : 1 ) ;
return ;
}
if ( bufferevent_socket_connect ( ctx - > srvdst . bev , ( struct sockaddr * ) & ctx - > dstaddr , ctx - > dstaddrlen ) = = - 1 ) {
log_err_level ( LOG_CRIT , " bufferevent_socket_connect for srvdst failed " ) ;
pxy_conn_free ( ctx , ctx - > term ? ctx - > term_requestor : 1 ) ;
}
}
# ifndef WITHOUT_USERAUTH
# if defined(__OpenBSD__) || defined(__linux__)
int
pxy_is_listuser ( userlist_t * list , const char * user
# ifdef DEBUG_PROXY
, pxy_conn_ctx_t * ctx , const char * listname
# endif /* DEBUG_PROXY */
)
{
while ( list ) {
if ( equal ( user , list - > user ) ) {
log_finest_va ( " User %s in %s " , user , listname ) ;
return 1 ;
}
list = list - > next ;
}
return 0 ;
}
void
pxy_classify_user ( pxy_conn_ctx_t * ctx )
{
if ( ctx - > spec - > opts - > passusers & & pxy_is_listuser ( ctx - > spec - > opts - > passusers , ctx - > user
# ifdef DEBUG_PROXY
, ctx , " PassUsers "
# endif /* DEBUG_PROXY */
) ) {
log_fine_va ( " User %s in PassUsers; engaging passthrough mode " , ctx - > user ) ;
protopassthrough_engage ( ctx ) ;
} else if ( ctx - > spec - > opts - > divertusers & & ! pxy_is_listuser ( ctx - > spec - > opts - > divertusers , ctx - > user
# ifdef DEBUG_PROXY
, ctx , " DivertUsers "
# endif /* DEBUG_PROXY */
) ) {
log_fine_va ( " User %s not in DivertUsers; terminating connection " , ctx - > user ) ;
pxy_conn_term ( ctx , 1 ) ;
}
}
static void
identify_user ( UNUSED evutil_socket_t fd , UNUSED short what , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
log_finest ( " ENTER " ) ;
if ( ctx - > ev ) {
event_free ( ctx - > ev ) ;
ctx - > ev = NULL ;
}
if ( ctx - > identify_user_count + + > = 50 ) {
log_finest ( " Cannot get conn user " ) ;
goto redirect ;
} else {
int rc ;
// @todo Do we really need to reset the stmt, as we always reset while returning?
sqlite3_reset ( ctx - > thr - > get_user ) ;
sqlite3_bind_text ( ctx - > thr - > get_user , 1 , ctx - > srchost_str , - 1 , NULL ) ;
rc = sqlite3_step ( ctx - > thr - > get_user ) ;
// Retry in case we cannot acquire db file or database: SQLITE_BUSY or SQLITE_LOCKED respectively
if ( rc = = SQLITE_BUSY | | rc = = SQLITE_LOCKED ) {
log_finest_va ( " User db busy or locked, retrying, count=%d " , ctx - > identify_user_count ) ;
// Do not forget to reset sqlite stmt, or else the userdb may remain busy/locked
sqlite3_reset ( ctx - > thr - > get_user ) ;
ctx - > ev = event_new ( ctx - > thr - > evbase , - 1 , 0 , identify_user , ctx ) ;
if ( ! ctx - > ev )
goto memout ;
struct timeval retry_delay = { 0 , 100 } ;
if ( event_add ( ctx - > ev , & retry_delay ) = = - 1 )
goto memout ;
return ;
} else if ( rc = = SQLITE_DONE ) {
log_finest ( " Conn has no user " ) ;
goto redirect ;
} else if ( rc = = SQLITE_ROW ) {
char * ether = ( char * ) sqlite3_column_text ( ctx - > thr - > get_user , 1 ) ;
if ( strncasecmp ( ether , ctx - > ether , 17 ) ) {
log_finest_va ( " Ethernet addresses do not match, db=%s, arp cache=%s " , ether , ctx - > ether ) ;
goto redirect ;
}
log_finest_va ( " Passed ethernet address test, %s " , ether ) ;
ctx - > idletime = time ( NULL ) - sqlite3_column_int ( ctx - > thr - > get_user , 2 ) ;
if ( ctx - > idletime > ctx - > conn_opts - > user_timeout ) {
log_finest_va ( " User entry timed out, idletime=%u " , ctx - > idletime ) ;
goto redirect ;
}
log_finest_va ( " Passed timeout test, idletime=%u " , ctx - > idletime ) ;
ctx - > user = strdup ( ( char * ) sqlite3_column_text ( ctx - > thr - > get_user , 0 ) ) ;
// Desc is needed for filtering
ctx - > desc = strdup ( ( char * ) sqlite3_column_text ( ctx - > thr - > get_user , 3 ) ) ;
if ( ! ctx - > user | | ! ctx - > desc ) {
goto memout ;
}
log_finest_va ( " Conn user=%s, desc=%s " , ctx - > user , ctx - > desc ) ;
ctx - > protoctx - > classify_usercb ( ctx ) ;
}
}
log_finest ( " Passed user identification " ) ;
redirect :
sqlite3_reset ( ctx - > thr - > get_user ) ;
if ( ctx - > ev ) {
event_free ( ctx - > ev ) ;
ctx - > ev = NULL ;
}
return ;
memout :
log_err_level_printf ( LOG_CRIT , " Aborting connection user identification! \n " ) ;
pxy_conn_term ( ctx , 1 ) ;
}
# endif /* __OpenBSD__ || __linux__ */
# ifdef __linux__
// Assume proc filesystem support
# define ARP_CACHE " / proc / net / arp"
/*
* We do not care about multiple matches or expiration status of arp cache entries on Linux .
*/
static int NONNULL ( 1 )
get_client_ether ( pxy_conn_ctx_t * ctx )
{
int rv = 0 ;
FILE * arp_cache = fopen ( ARP_CACHE , " r " ) ;
if ( ! arp_cache ) {
log_err_level_printf ( LOG_CRIT , " Failed to open arp cache: \" " ARP_CACHE " \" \n " ) ;
return - 1 ;
}
// Skip the first line, which contains the header
char header [ 1024 ] ;
if ( ! fgets ( header , sizeof ( header ) , arp_cache ) ) {
log_err_level_printf ( LOG_CRIT , " Failed to skip arp cache header \n " ) ;
rv = - 1 ;
goto out ;
}
char ip [ 46 ] , ether [ 18 ] ;
//192.168.0.1 0x1 0x2 00:50:56:2c:bf:e0 * enp3s0f1
while ( fscanf ( arp_cache , " %45s %*s %*s %17s %*s %*s " , ip , ether ) = = 2 ) {
if ( ! strncasecmp ( ip , ctx - > srchost_str , 45 ) ) {
log_finest_va ( " Arp entry for %s: %s " , ip , ether ) ;
ctx - > ether = strdup ( ether ) ;
rv = 1 ;
goto out ;
}
}
out :
fclose ( arp_cache ) ;
return rv ;
}
# endif /* __linux__ */
# ifdef __OpenBSD__
/*
* This is a modified version of the same function from OpenBSD sources ,
* which has a 3 - clause BSD license .
*/
static char *
ether_str ( struct sockaddr_dl * sdl )
{
char hbuf [ NI_MAXHOST ] ;
u_char * cp ;
if ( sdl - > sdl_alen ) {
cp = ( u_char * ) LLADDR ( sdl ) ;
snprintf ( hbuf , sizeof ( hbuf ) , " %02x:%02x:%02x:%02x:%02x:%02x " ,
cp [ 0 ] , cp [ 1 ] , cp [ 2 ] , cp [ 3 ] , cp [ 4 ] , cp [ 5 ] ) ;
return strdup ( hbuf ) ;
} else {
return NULL ;
}
}
/*
* This is a modified version of a similar function from OpenBSD sources ,
* which has a 3 - clause BSD license .
*/
static int NONNULL ( 2 )
get_client_ether ( in_addr_t addr , pxy_conn_ctx_t * ctx )
{
int mib [ 7 ] ;
size_t needed ;
char * lim , * buf = NULL , * next ;
struct rt_msghdr * rtm ;
struct sockaddr_inarp * sin ;
struct sockaddr_dl * sdl ;
int found_entry = 0 ;
int rdomain = getrtable ( ) ;
mib [ 0 ] = CTL_NET ;
mib [ 1 ] = PF_ROUTE ;
mib [ 2 ] = 0 ;
mib [ 3 ] = AF_INET ;
mib [ 4 ] = NET_RT_FLAGS ;
mib [ 5 ] = RTF_LLINFO ;
mib [ 6 ] = rdomain ;
while ( 1 ) {
if ( sysctl ( mib , 7 , NULL , & needed , NULL , 0 ) = = - 1 ) {
log_err_level_printf ( LOG_WARNING , " route-sysctl-estimate \n " ) ;
}
if ( needed = = 0 ) {
return found_entry ;
}
if ( ( buf = realloc ( buf , needed ) ) = = NULL ) {
return - 1 ;
}
if ( sysctl ( mib , 7 , buf , & needed , NULL , 0 ) = = - 1 ) {
if ( errno = = ENOMEM )
continue ;
log_finest ( " actual retrieval of routing table " ) ;
}
lim = buf + needed ;
break ;
}
int expired = 0 ;
int incomplete = 0 ;
for ( next = buf ; next < lim ; next + = rtm - > rtm_msglen ) {
rtm = ( struct rt_msghdr * ) next ;
if ( rtm - > rtm_version ! = RTM_VERSION )
continue ;
sin = ( struct sockaddr_inarp * ) ( next + rtm - > rtm_hdrlen ) ;
sdl = ( struct sockaddr_dl * ) ( sin + 1 ) ;
if ( addr ) {
if ( addr ! = sin - > sin_addr . s_addr )
continue ;
found_entry + + ;
}
UNUSED char * expire = NULL ;
if ( rtm - > rtm_flags & ( RTF_PERMANENT_ARP | RTF_LOCAL ) ) {
expire = " permanent " ;
} else if ( rtm - > rtm_rmx . rmx_expire = = 0 ) {
expire = " static " ;
} else if ( rtm - > rtm_rmx . rmx_expire > time ( NULL ) ) {
expire = " active " ;
} else {
expire = " expired " ;
expired + + ;
}
char * ether = ether_str ( sdl ) ;
if ( ether ) {
// Record the first unexpired complete entry
if ( ! ctx - > ether & & ( found_entry - expired ) = = 1 ) {
log_finest_va ( " Arp entry for %s: %s " , inet_ntoa ( sin - > sin_addr ) , ether ) ;
// Dup before assignment because we free local var ether below
ctx - > ether = strdup ( ether ) ;
// Do not care about multiple matches, return immediately
free ( ether ) ;
goto out ;
}
} else {
incomplete + + ;
}
log_finest_va ( " Arp entry %u for %s: %s (%s) " , found_entry , inet_ntoa ( sin - > sin_addr ) , ether ? ether : " incomplete " , expire ) ;
if ( ether ) {
free ( ether ) ;
}
}
out :
free ( buf ) ;
return found_entry - expired - incomplete ;
}
# endif /* __OpenBSD__ */
void
pxy_userauth ( pxy_conn_ctx_t * ctx )
{
if ( ctx - > conn_opts - > user_auth & & ! ctx - > user ) {
# if defined(__OpenBSD__) || defined(__linux__)
int ec = get_client_ether (
# if defined(__OpenBSD__)
( ( struct sockaddr_in * ) & ctx - > srcaddr ) - > sin_addr . s_addr ,
# endif /* __OpenBSD__ */
ctx ) ;
if ( ec = = 1 ) {
identify_user ( - 1 , 0 , ctx ) ;
return ;
} else if ( ec = = 0 ) {
log_err_level_printf ( LOG_CRIT , " Cannot find ethernet address of client IP address \n " ) ;
} else if ( ec > 1 ) {
// get_client_ether() does not return multiple matches, but keep this in case a future version does
log_err_level_printf ( LOG_CRIT , " Multiple ethernet addresses for the same client IP address \n " ) ;
} else {
// ec == -1
log_err_level_printf ( LOG_CRIT , " Aborting connection setup (out of memory)! \n " ) ;
}
# endif /* __OpenBSD__ || __linux__ */
log_err_level_printf ( LOG_CRIT , " Aborting connection setup (user auth)! \n " ) ;
pxy_conn_term ( ctx , 1 ) ;
}
}
# endif /* !WITHOUT_USERAUTH */
int
pxy_conn_apply_deferred_block_action ( pxy_conn_ctx_t * ctx )
{
if ( ctx - > deferred_action & FILTER_ACTION_BLOCK ) {
log_fine ( " Applying deferred block action " ) ;
pxy_conn_term ( ctx , 1 ) ;
return 1 ;
}
return 0 ;
}
unsigned int
pxy_conn_translate_filter_action ( pxy_conn_ctx_t * ctx , filter_action_t * a )
{
unsigned int action = FILTER_ACTION_NONE ;
if ( a - > divert ) {
action = FILTER_ACTION_DIVERT ;
}
else if ( a - > split ) {
action = FILTER_ACTION_SPLIT ;
}
else if ( a - > pass ) {
// Ignore pass action if already in passthrough mode
if ( ! ctx - > pass ) {
action = FILTER_ACTION_PASS ;
}
}
else if ( a - > block ) {
action = FILTER_ACTION_BLOCK ;
}
else if ( a - > match ) {
action = FILTER_ACTION_MATCH ;
}
// Multiple log actions can be defined, hence no 'else'
// 0: don't change, 1: disable, 2: enable
if ( a - > log_connect ) {
action | = ( a - > log_connect % 2 ) ? FILTER_LOG_NOCONNECT : FILTER_LOG_CONNECT ;
}
if ( a - > log_master ) {
action | = ( a - > log_master % 2 ) ? FILTER_LOG_NOMASTER : FILTER_LOG_MASTER ;
}
if ( a - > log_cert ) {
action | = ( a - > log_cert % 2 ) ? FILTER_LOG_NOCERT : FILTER_LOG_CERT ;
}
if ( a - > log_content ) {
action | = ( a - > log_content % 2 ) ? FILTER_LOG_NOCONTENT : FILTER_LOG_CONTENT ;
}
if ( a - > log_pcap ) {
action | = ( a - > log_pcap % 2 ) ? FILTER_LOG_NOPCAP : FILTER_LOG_PCAP ;
}
# ifndef WITHOUT_MIRROR
if ( a - > log_mirror ) {
action | = ( a - > log_mirror % 2 ) ? FILTER_LOG_NOMIRROR : FILTER_LOG_MIRROR ;
}
# endif /* !WITHOUT_MIRROR */
action | = a - > precedence ;
return action ;
}
filter_action_t *
pxy_conn_set_filter_action ( filter_action_t * a1 , filter_action_t * a2
# ifdef DEBUG_PROXY
, pxy_conn_ctx_t * ctx , char * s1 , char * s2
# endif /* DEBUG_PROXY */
)
{
filter_action_t * a ;
# ifdef DEBUG_PROXY
char * site ;
# endif /* DEBUG_PROXY */
// a1 has precedence over a2, unless a2's precedence is higher
if ( ! a1 | | ( a1 & & a2 & & ( a1 - > precedence < a2 - > precedence ) ) ) {
a = a2 ;
# ifdef DEBUG_PROXY
site = s2 ;
if ( a1 & & a2 & & ( a1 - > precedence < a2 - > precedence ) )
log_finest_va ( " Rule 2 has higher precedence than rule 1: %d > %d (line=%d, %d), %s, %s " , a2 - > precedence , a1 - > precedence , a2 - > line_num , a1 - > line_num , s2 , s1 ) ;
# endif /* DEBUG_PROXY */
} else {
a = a1 ;
# ifdef DEBUG_PROXY
site = s1 ;
# endif /* DEBUG_PROXY */
}
# ifdef DEBUG_PROXY
if ( a - > divert ) {
log_fine_va ( " Filter divert action for %s, precedence %d (line=%d) " , site , a - > precedence , a - > line_num ) ;
}
else if ( a - > split ) {
log_fine_va ( " Filter split action for %s, precedence %d (line=%d) " , site , a - > precedence , a - > line_num ) ;
}
else if ( a - > pass ) {
// Ignore pass action if already in passthrough mode
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
if ( ! ctx - > pass ) {
log_fine_va ( " Filter pass action for %s, precedence %d (line=%d) " , site , a - > precedence , a - > line_num ) ;
}
}
else if ( a - > block ) {
log_fine_va ( " Filter block action for %s, precedence %d (line=%d) " , site , a - > precedence , a - > line_num ) ;
}
else if ( a - > match ) {
log_fine_va ( " Filter match action for %s, precedence %d (line=%d) " , site , a - > precedence , a - > line_num ) ;
}
// Multiple log actions can be defined, hence no 'else'
// 0: don't change, 1: disable, 2: enable
if ( a - > log_connect ) {
log_fine_va ( " Filter %s connect log for %s, precedence %d (line=%d) " , a - > log_connect % 2 ? " disable " : " enable " , site , a - > precedence , a - > line_num ) ;
}
if ( a - > log_master ) {
log_fine_va ( " Filter %s master log for %s, precedence %d (line=%d) " , a - > log_master % 2 ? " disable " : " enable " , site , a - > precedence , a - > line_num ) ;
}
if ( a - > log_cert ) {
log_fine_va ( " Filter %s cert log for %s, precedence %d (line=%d) " , a - > log_cert % 2 ? " disable " : " enable " , site , a - > precedence , a - > line_num ) ;
}
if ( a - > log_content ) {
log_fine_va ( " Filter %s content log for %s, precedence %d (line=%d) " , a - > log_content % 2 ? " disable " : " enable " , site , a - > precedence , a - > line_num ) ;
}
if ( a - > log_pcap ) {
log_fine_va ( " Filter %s pcap log for %s, precedence %d (line=%d) " , a - > log_pcap % 2 ? " disable " : " enable " , site , a - > precedence , a - > line_num ) ;
}
# ifndef WITHOUT_MIRROR
if ( a - > log_mirror ) {
log_fine_va ( " Filter %s mirror log for %s, precedence %d (line=%d) " , a - > log_mirror % 2 ? " disable " : " enable " , site , a - > precedence , a - > line_num ) ;
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
}
# endif /* !WITHOUT_MIRROR */
# endif /* DEBUG_PROXY */
return a ;
}
static int NONNULL ( 1 , 2 )
pxy_conn_filter_match_port ( pxy_conn_ctx_t * ctx , filter_port_t * port )
{
if ( port - > action . precedence < ctx - > filter_precedence ) {
log_finest_va ( " Rule port precedence lower than conn filter precedence %d < %d (line=%d): %s, %s " , port - > action . precedence , ctx - > filter_precedence , port - > action . line_num , port - > port , ctx - > dsthost_str ) ;
return 0 ;
}
# ifdef DEBUG_PROXY
if ( port - > all_ports )
log_finest_va ( " Match all dst ports (line=%d): %s, %s " , port - > action . line_num , port - > port , ctx - > dstport_str ) ;
else if ( port - > exact )
log_finest_va ( " Match exact with dst port (line=%d): %s, %s " , port - > action . line_num , port - > port , ctx - > dstport_str ) ;
else
log_finest_va ( " Match substring in dst port (line=%d): %s, %s " , port - > action . line_num , port - > port , ctx - > dstport_str ) ;
# endif /* DEBUG_PROXY */
return 1 ;
}
filter_action_t *
pxy_conn_filter_port ( pxy_conn_ctx_t * ctx , filter_site_t * site )
{
filter_port_t * port = filter_port_find ( site , ctx - > dstport_str ) ;
if ( port ) {
log_fine_va ( " Found port (line=%d): %s for %s:%s, %s:%s " , port - > action . line_num , port - > port ,
STRORDASH ( ctx - > srchost_str ) , STRORDASH ( ctx - > srcport_str ) , STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) ) ;
if ( pxy_conn_filter_match_port ( ctx , port ) )
return & port - > action ;
}
else
log_finest_va ( " No filter match with port: %s:%s, %s:%s " ,
STRORDASH ( ctx - > srchost_str ) , STRORDASH ( ctx - > srcport_str ) , STRORDASH ( ctx - > dsthost_str ) , STRORDASH ( ctx - > dstport_str ) ) ;
return NULL ;
}
# ifndef WITHOUT_USERAUTH
static filter_action_t *
pxy_conn_filter_user ( pxy_conn_ctx_t * ctx , proto_filter_func_t filtercb , filter_user_t * user )
{
filter_action_t * action = NULL ;
if ( user ) {
if ( ctx - > desc ) {
log_finest_va ( " Searching user keyword exact: %s, %s " , ctx - > user , ctx - > desc ) ;
filter_desc_t * keyword = filter_desc_exact_match ( user - > desc_btree , ctx - > desc ) ;
if ( keyword & & ( action = filtercb ( ctx , keyword - > list ) ) ) {
return action ;
}
log_finest_va ( " Searching user keyword substring: %s, %s " , ctx - > user , ctx - > desc ) ;
keyword = filter_desc_substring_match ( user - > desc_acm , ctx - > desc ) ;
if ( keyword & & ( action = filtercb ( ctx , keyword - > list ) ) ) {
return action ;
}
}
if ( ( action = filtercb ( ctx , user - > list ) ) ) {
return action ;
}
}
return action ;
}
# endif /* !WITHOUT_USERAUTH */
filter_action_t *
pxy_conn_filter ( pxy_conn_ctx_t * ctx , proto_filter_func_t filtercb )
{
filter_action_t * action = NULL ;
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
filter_t * filter = ctx - > spec - > opts - > filter ;
if ( filter ) {
# ifndef WITHOUT_USERAUTH
if ( ctx - > user ) {
log_finest_va ( " Searching user exact: %s " , ctx - > user ) ;
filter_user_t * user = filter_user_exact_match ( filter - > user_btree , ctx - > user ) ;
if ( ( action = pxy_conn_filter_user ( ctx , filtercb , user ) ) )
return action ;
log_finest_va ( " Searching user substring: %s " , ctx - > user ) ;
user = filter_user_substring_match ( filter - > user_acm , ctx - > user ) ;
if ( ( action = pxy_conn_filter_user ( ctx , filtercb , user ) ) )
return action ;
if ( ctx - > desc ) {
log_finest_va ( " Searching keyword exact: %s " , ctx - > desc ) ;
filter_desc_t * keyword = filter_desc_exact_match ( filter - > desc_btree , ctx - > desc ) ;
if ( keyword & & ( action = filtercb ( ctx , keyword - > list ) ) ) {
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
return action ;
}
log_finest_va ( " Searching keyword substring: %s, %s " , ctx - > user , ctx - > desc ) ;
keyword = filter_desc_substring_match ( filter - > desc_acm , ctx - > desc ) ;
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
if ( keyword & & ( action = filtercb ( ctx , keyword - > list ) ) ) {
return action ;
}
}
log_finest ( " Searching all_user " ) ;
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
if ( filter - > all_user & & ( action = filtercb ( ctx , filter - > all_user ) ) ) {
return action ;
}
}
# endif /* !WITHOUT_USERAUTH */
if ( ctx - > srchost_str ) {
log_finest_va ( " Searching ip exact: %s " , ctx - > srchost_str ) ;
filter_ip_t * ip = filter_ip_exact_match ( filter - > ip_btree , ctx - > srchost_str ) ;
if ( ip & & ( action = filtercb ( ctx , ip - > list ) ) ) {
return action ;
}
log_finest_va ( " Searching ip substring: %s " , ctx - > srchost_str ) ;
ip = filter_ip_substring_match ( filter - > ip_acm , ctx - > srchost_str ) ;
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
if ( ip & & ( action = filtercb ( ctx , ip - > list ) ) ) {
return action ;
}
}
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
log_finest ( " Searching all " ) ;
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
if ( filter - > all & & ( action = filtercb ( ctx , filter - > all ) ) ) {
return action ;
}
}
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
return action ;
}
int
pxy_conn_init ( pxy_conn_ctx_t * ctx )
{
log_finest ( " ENTER " ) ;
pxy_thr_attach ( ctx ) ;
ctx - > ctime = time ( NULL ) ;
ctx - > atime = ctx - > ctime ;
if ( check_fd_usage (
# ifdef DEBUG_PROXY
ctx
# endif /* DEBUG_PROXY */
) = = - 1 ) {
goto out ;
}
ctx - > af = ctx - > srcaddr . ss_family ;
/* determine original destination of connection */
if ( ctx - > spec - > natlookup ) {
/* NAT engine lookup */
ctx - > dstaddrlen = sizeof ( struct sockaddr_storage ) ;
if ( ctx - > spec - > natlookup ( ( struct sockaddr * ) & ctx - > dstaddr , & ctx - > dstaddrlen , ctx - > fd , ( struct sockaddr * ) & ctx - > srcaddr , ctx - > srcaddrlen ) = = - 1 ) {
log_err_printf ( " Connection not found in NAT state table, aborting connection \n " ) ;
goto out ;
}
} else if ( ctx - > spec - > connect_addrlen > 0 ) {
/* static forwarding */
ctx - > dstaddrlen = ctx - > spec - > connect_addrlen ;
memcpy ( & ctx - > dstaddr , & ctx - > spec - > connect_addr , ctx - > dstaddrlen ) ;
} else {
/* SNI mode */
if ( ! ctx - > spec - > ssl ) {
/* if this happens, the proxyspec parser is broken */
log_err_printf ( " SNI mode used for non-SSL connection; aborting connection \n " ) ;
goto out ;
}
}
if ( sys_sockaddr_str ( ( struct sockaddr * ) & ctx - > srcaddr , ctx - > srcaddrlen , & ctx - > srchost_str , & ctx - > srcport_str ) ! = 0 ) {
log_err_level_printf ( LOG_CRIT , " Aborting connection setup (out of memory)! \n " ) ;
goto out ;
}
log_finest_va ( " srcaddr= [%s]:%s " , ctx - > srchost_str , ctx - > srcport_str ) ;
return 0 ;
out :
evutil_closesocket ( ctx - > fd ) ;
pxy_conn_ctx_free ( ctx , 1 ) ;
return - 1 ;
}
/* vim: set noet ft=c: */