Skip to content

Commit

Permalink
Added proto function for extra conn matching.
Browse files Browse the repository at this point in the history
A TCP-based protocol may export an API function for performing extra checks when comes to matching and re-using existing connections.
For now the SSL using modules (TLS & WSS) are using the function to perform SSL cert matching, to avoid re-using connections with wrong SSL certificate.
Fixes #1651
  • Loading branch information
bogdan-iancu committed May 28, 2019
1 parent 3ed991e commit e414655
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 27 deletions.
4 changes: 2 additions & 2 deletions modules/proto_bin/proto_bin.c
Expand Up @@ -522,9 +522,9 @@ static int proto_bin_send(struct socket_info* send_sock,
if (to){
su2ip_addr(&ip, to);
port=su_getport(to);
n = tcp_conn_get(id, &ip, port, PROTO_BIN, &c, &fd);
n = tcp_conn_get(id, &ip, port, PROTO_BIN, NULL, &c, &fd);
}else if (id){
n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd);
n = tcp_conn_get(id, 0, 0, PROTO_NONE, NULL, &c, &fd);
}else{
LM_CRIT("tcp_send called with null id & to\n");
return -1;
Expand Down
4 changes: 2 additions & 2 deletions modules/proto_hep/proto_hep.c
Expand Up @@ -703,9 +703,9 @@ static int hep_tcp_send (struct socket_info* send_sock,
if (to) {
su2ip_addr(&ip, to);
port=su_getport(to);
n = tcp_conn_get(id,&ip, port, PROTO_HEP_TCP, &c, &fd);
n = tcp_conn_get(id,&ip, port, PROTO_HEP_TCP, NULL, &c, &fd);
} else if (id) {
n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd);
n = tcp_conn_get(id, 0, 0, PROTO_NONE, NULL, &c, &fd);
} else {
LM_CRIT("tcp_send called with null id & to\n");
return -1;
Expand Down
3 changes: 2 additions & 1 deletion modules/proto_smpp/smpp.c
Expand Up @@ -517,7 +517,8 @@ static int smpp_send_msg(smpp_session_t *smsc, str *buffer)
/* first try to acquire the connection */

/* TBD - handle conn not found here = reconnect ? */
ret = tcp_conn_get(smsc->conn_id, &smsc->ip, smsc->port, PROTO_SMPP, &conn, &fd);
ret = tcp_conn_get(smsc->conn_id, &smsc->ip, smsc->port, PROTO_SMPP,
NULL, &conn, &fd);
if (ret <= 0) {
LM_ERR("cannot fetch connection for %.*s (%d)\n",
smsc->name.len, smsc->name.s, ret);
Expand Down
32 changes: 32 additions & 0 deletions modules/proto_tls/doc/proto_tls_admin.xml
Expand Up @@ -325,6 +325,38 @@ modparam("proto_tls", "tls_max_msg_chunks", 8)
</programlisting>
</example>
</section>

<section id="param_tls_cert_check_on_conn_reusage" xreflabel="cert_check_on_conn_reusage">
<title><varname>cert_check_on_conn_reusage</varname> (integer)</title>
<para>
This parameter turns on or off the extra checking/matching of the
TLS domain (SSL certificate) when comes to reusing an existing TLS
connection. Without this extra check, only IP and port of the
connections will be check (in order to re-use an existing connection).
With this extra check, the connection to be reused must have the same
SSL certificate as the one set for the current signaling operation.
</para>
<para>
This checking is done only when comes to send SIP traffic via TLS and
it is applied only against connections that were created / initiated
by OpenSIPS (as TLS client). Any accepte connection (as TLS server) will
automatically match (the extra test will be skipped).
</para>
<para>
<emphasis>
Default value is 0 (disabled).
</emphasis>
</para>
<example>
<title>Set <varname>cert_check_on_conn_reusage</varname> parameter</title>
<programlisting format="linespecific">
...
modparam("proto_tls", "cert_check_on_conn_reusage", 1)
...
</programlisting>
</example>
</section>

<section id="trace-destination">
<title><varname>trace_destination</varname> (string)</title>
<para>
Expand Down
21 changes: 17 additions & 4 deletions modules/proto_tls/proto_tls.c
Expand Up @@ -101,6 +101,9 @@ static int tls_crlf_pingpong = 1;
/* 0: do not drop single CRLF messages */
static int tls_crlf_drop = 0;

/* check the SSL certificate when comes to TCP conn reusage */
static int cert_check_on_conn_reusage = 0;

static int mod_init(void);
static void mod_destroy(void);
static int proto_tls_init(struct proto_info *pi);
Expand Down Expand Up @@ -166,8 +169,9 @@ static param_export_t params[] = {
{ "tls_crlf_drop", INT_PARAM, &tls_crlf_drop },
{ "tls_max_msg_chunks", INT_PARAM, &tls_max_msg_chunks },
{ "trace_destination", STR_PARAM, &trace_destination_name.s },
{ "trace_on", INT_PARAM, &trace_is_on_tmp },
{ "trace_filter_route", STR_PARAM, &trace_filter_route },
{ "trace_on", INT_PARAM, &trace_is_on_tmp },
{ "trace_filter_route", STR_PARAM, &trace_filter_route },
{ "cert_check_on_conn_reusage", INT_PARAM, &cert_check_on_conn_reusage},
{0, 0, 0}
};

Expand Down Expand Up @@ -289,6 +293,10 @@ static int proto_tls_init(struct proto_info *pi)
pi->net.read = (proto_net_read_f)tls_read_req;
pi->net.conn_init = proto_tls_conn_init;
pi->net.conn_clean = proto_tls_conn_clean;
if (cert_check_on_conn_reusage)
pi->net.conn_match = tls_conn_extra_match;
else
pi->net.conn_match = NULL;
pi->net.report = tls_report;

return 0;
Expand Down Expand Up @@ -431,16 +439,21 @@ static int proto_tls_send(struct socket_info* send_sock,
char* buf, unsigned int len, union sockaddr_union* to, int id)
{
struct tcp_connection *c;
struct tls_domain *dom;
struct ip_addr ip;
int port;
int fd, n;

if (to){
su2ip_addr(&ip, to);
port=su_getport(to);
n = tcp_conn_get(id, &ip, port, PROTO_TLS, &c, &fd);
dom = (cert_check_on_conn_reusage==0)?
NULL : tls_mgm_api.find_client_domain( &ip, port);
n = tcp_conn_get(id, &ip, port, PROTO_TLS, dom?dom->ctx:NULL, &c, &fd);
if (dom)
tls_mgm_api.release_domain(dom);
}else if (id){
n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd);
n = tcp_conn_get(id, 0, 0, PROTO_NONE, NULL, &c, &fd);
}else{
LM_CRIT("prot_tls_send called with null id & to\n");
return -1;
Expand Down
4 changes: 2 additions & 2 deletions modules/proto_ws/proto_ws.c
Expand Up @@ -416,9 +416,9 @@ static int proto_ws_send(struct socket_info* send_sock,
if (to){
su2ip_addr(&ip, to);
port=su_getport(to);
n = tcp_conn_get(id, &ip, port, PROTO_WS, &c, &fd);
n = tcp_conn_get(id, &ip, port, PROTO_WS, NULL, &c, &fd);
}else if (id){
n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd);
n = tcp_conn_get(id, 0, 0, PROTO_NONE, NULL, &c, &fd);
}else{
LM_CRIT("prot_tls_send called with null id & to\n");
get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
Expand Down
32 changes: 32 additions & 0 deletions modules/proto_wss/doc/proto_wss_admin.xml
Expand Up @@ -206,6 +206,38 @@ modparam("proto_wss", "wss_handshake_timeout", 300)
</programlisting>
</example>
</section>

<section id="param_tls_cert_check_on_conn_reusage" xreflabel="cert_check_on_conn_reusage">
<title><varname>cert_check_on_conn_reusage</varname> (integer)</title>
<para>
This parameter turns on or off the extra checking/matching of the
TLS domain (SSL certificate) when comes to reusing an existing TLS
connection. Without this extra check, only IP and port of the
connections will be check (in order to re-use an existing connection).
With this extra check, the connection to be reused must have the same
SSL certificate as the one set for the current signaling operation.
</para>
<para>
This checking is done only when comes to send SIP traffic via TLS and
it is applied only against connections that were created / initiated
by OpenSIPS (as TLS client). Any accepte connection (as TLS server) will
automatically match (the extra test will be skipped).
</para>
<para>
<emphasis>
Default value is 0 (disabled).
</emphasis>
</para>
<example>
<title>Set <varname>cert_check_on_conn_reusage</varname> parameter</title>
<programlisting format="linespecific">
...
modparam("proto_wss", "cert_check_on_conn_reusage", 1)
...
</programlisting>
</example>
</section>

<section id="param_trace_destination" xreflabel="trace_destination">
<title><varname>trace_destination</varname> (string)</title>
<para>
Expand Down
23 changes: 18 additions & 5 deletions modules/proto_wss/proto_wss.c
Expand Up @@ -61,6 +61,9 @@ static struct ws_req wss_current_req;

int wss_hs_read_tout = 100;

/* check the SSL certificate when comes to TCP conn reusage */
static int cert_check_on_conn_reusage = 0;

/* XXX: this information should be dynamically provided */
static str wss_resource = str_init("/");

Expand Down Expand Up @@ -108,6 +111,7 @@ static int wss_conn_init(struct tcp_connection* c);
static void ws_conn_clean(struct tcp_connection* c);
static void wss_report(int type, unsigned long long conn_id, int conn_flags,
void *extra);

static mi_response_t *wss_trace_mi(const mi_params_t *params,
struct mi_handler *async_hdl);
static mi_response_t *wss_trace_mi_1(const mi_params_t *params,
Expand All @@ -127,8 +131,9 @@ static param_export_t params[] = {
{ "wss_resource", STR_PARAM, &wss_resource },
{ "wss_handshake_timeout", INT_PARAM, &wss_hs_read_tout},
{ "trace_destination", STR_PARAM, &trace_destination_name.s },
{ "trace_on", INT_PARAM, &trace_is_on_tmp },
{ "trace_filter_route", STR_PARAM, &trace_filter_route },
{ "trace_on", INT_PARAM, &trace_is_on_tmp },
{ "trace_filter_route", STR_PARAM, &trace_filter_route },
{ "cert_check_on_conn_reusage", INT_PARAM, &cert_check_on_conn_reusage},
{0, 0, 0}
};

Expand Down Expand Up @@ -191,6 +196,10 @@ static int proto_wss_init(struct proto_info *pi)

pi->net.conn_init = wss_conn_init;
pi->net.conn_clean = ws_conn_clean;
if (cert_check_on_conn_reusage)
pi->net.conn_match = tls_conn_extra_match;
else
pi->net.conn_match = NULL;
pi->net.report = wss_report;

return 0;
Expand Down Expand Up @@ -425,13 +434,13 @@ static struct tcp_connection* ws_connect(struct socket_info* send_sock,
/************** WRITE related functions ***************/



/*! \brief Finds a tcpconn & sends on it */
static int proto_wss_send(struct socket_info* send_sock,
char* buf, unsigned int len,
union sockaddr_union* to, int id)
{
struct tcp_connection *c;
struct tls_domain *dom;
struct timeval get;
struct ip_addr ip;
int port = 0;
Expand All @@ -444,9 +453,13 @@ static int proto_wss_send(struct socket_info* send_sock,
if (to){
su2ip_addr(&ip, to);
port=su_getport(to);
n = tcp_conn_get(id, &ip, port, PROTO_WSS, &c, &fd);
dom = (cert_check_on_conn_reusage==0)?
NULL : tls_mgm_api.find_client_domain( &ip, port);
n = tcp_conn_get(id, &ip, port, PROTO_WSS, dom?dom->ctx:NULL, &c, &fd);
if (dom)
tls_mgm_api.release_domain(dom);
}else if (id){
n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd);
n = tcp_conn_get(id, 0, 0, PROTO_NONE, NULL, &c, &fd);
}else{
LM_CRIT("prot_tls_send called with null id & to\n");
get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
Expand Down
9 changes: 9 additions & 0 deletions modules/tls_mgm/tls_conn_ops.h
Expand Up @@ -287,5 +287,14 @@ static int tls_read(struct tcp_connection * c,struct tcp_req *r)
}


static int tls_conn_extra_match(struct tcp_connection *c, void *id)
{
if ( (c->flags&F_CONN_ACCEPTED) ||
(SSL_get_SSL_CTX((SSL*)c->extra_data) == id) )
return 1; /*true*/

return 0; /*false*/
}

#endif /* TLS_CONN_OPS_H */

8 changes: 3 additions & 5 deletions modules/tls_mgm/tls_mgm.c
Expand Up @@ -2075,11 +2075,9 @@ static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2)
}

LM_DBG("trying to find TCP connection of received message...\n");
/* what if we have multiple connections to the same remote socket? e.g. we can have
connection 1: localIP1:localPort1 <--> remoteIP:remotePort
connection 2: localIP2:localPort2 <--> remoteIP:remotePort
but I think the is very unrealistic */
tcp_conn_get(0, &(msg->rcv.src_ip), msg->rcv.src_port, PROTO_TLS, &c, NULL/*fd*/);

tcp_conn_get( msg->rcv.proto_reserved1, 0, 0, PROTO_TLS, NULL,
&c, NULL/*fd*/);
if (c==NULL) {
LM_ERR("no corresponding TLS/TCP connection found."
" This should not happen... return -1\n");
Expand Down
4 changes: 3 additions & 1 deletion modules/tls_mgm/tls_select.c
Expand Up @@ -54,7 +54,9 @@ struct tcp_connection* get_cur_connection(struct sip_msg* msg)
return 0;
}

tcp_conn_get(msg->rcv.proto_reserved1, 0, 0, PROTO_NONE, &c, NULL/*fd*/);
/* get conn by ID */
tcp_conn_get(msg->rcv.proto_reserved1, 0, 0, PROTO_NONE, NULL,
&c, NULL/*fd*/);
if (c && c->type != PROTO_TLS) {
LM_ERR("connection found but is not TLS (bug in config)\n");
tcp_conn_release(c, 0);
Expand Down
2 changes: 2 additions & 0 deletions net/api_proto_net.h
Expand Up @@ -37,6 +37,7 @@ typedef int (*proto_net_write_f)(void *src, int fd);
typedef int (*proto_net_read_f)(void *src, int *len);
typedef int (*proto_net_conn_init_f)(struct tcp_connection *c);
typedef void (*proto_net_conn_clean_f)(struct tcp_connection *c);
typedef int (*proto_net_extra_match_f)(struct tcp_connection *c, void *id);
typedef void (*proto_net_report_f)( int type, unsigned long long conn_id,
int conn_flags, void *extra);

Expand All @@ -46,6 +47,7 @@ struct api_proto_net {
proto_net_read_f read;
proto_net_conn_init_f conn_init;
proto_net_conn_clean_f conn_clean;
proto_net_extra_match_f conn_match;
proto_net_report_f report;
};

Expand Down
7 changes: 5 additions & 2 deletions net/net_tcp.c
Expand Up @@ -544,7 +544,7 @@ int tcp_get_correlation_id( int id, unsigned long long *cid)

/*! \brief _tcpconn_find with locks and acquire fd */
int tcp_conn_get(int id, struct ip_addr* ip, int port, enum sip_protos proto,
struct tcp_connection** conn, int* conn_fd)
void *proto_extra_id, struct tcp_connection** conn, int* conn_fd)
{
struct tcp_connection* c;
struct tcp_connection* tmp;
Expand Down Expand Up @@ -583,7 +583,10 @@ int tcp_conn_get(int id, struct ip_addr* ip, int port, enum sip_protos proto,
if (c->state != S_CONN_BAD &&
port == a->port &&
proto == c->type &&
ip_addr_cmp(ip, &c->rcv.src_ip))
ip_addr_cmp(ip, &c->rcv.src_ip) &&
(proto_extra_id==NULL ||
protos[proto].net.conn_match==NULL ||
protos[proto].net.conn_match( c, proto_extra_id)) )
goto found;
}
TCPCONN_UNLOCK(part);
Expand Down
2 changes: 1 addition & 1 deletion net/net_tcp.h
Expand Up @@ -91,7 +91,7 @@ int tcp_connect_blocking_timeout(int s, const struct sockaddr *servaddr,

/* returns the connection identified by either the id or the destination to */
int tcp_conn_get(int id, struct ip_addr* ip, int port, enum sip_protos proto,
struct tcp_connection** conn, int* conn_fd);
void *proto_extra_id, struct tcp_connection** conn, int* conn_fd);

/* creates a new tcp conn around a newly connected socket
* and sends it to the master */
Expand Down
4 changes: 2 additions & 2 deletions net/proto_tcp/proto_tcp.c
Expand Up @@ -778,9 +778,9 @@ static int proto_tcp_send(struct socket_info* send_sock,
if (to){
su2ip_addr(&ip, to);
port=su_getport(to);
n = tcp_conn_get(id, &ip, port, PROTO_TCP, &c, &fd);
n = tcp_conn_get(id, &ip, port, PROTO_TCP, NULL, &c, &fd);
}else if (id){
n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd);
n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, NULL, &fd);
}else{
LM_CRIT("tcp_send called with null id & to\n");
get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
Expand Down

0 comments on commit e414655

Please sign in to comment.