diff --git a/modules/tls_mgm/README b/modules/tls_mgm/README
index bce74783b08..79f105b4db5 100644
--- a/modules/tls_mgm/README
+++ b/modules/tls_mgm/README
@@ -58,6 +58,8 @@ Marius Cristian Eseanu
1.5. Exported Functions
1.5.1. is_peer_verified
+ 1.5.2. tls_check_to
+ 1.5.3. tls_check_from
1.6. Exported MI Functions
@@ -199,6 +201,8 @@ Marius Cristian Eseanu
1.32. Example of $tls_[peer|my]_[subject|issuer]
1.33. Script with TLS support
1.34. Example of TLS logging
+ 1.35. Example of tls_check_to
+ 1.36. Example of tls_check_from
Chapter 1. Admin Guide
@@ -272,6 +276,44 @@ if (is_peer_verified()) {
}
...
+1.5.2. tls_check_to
+
+ Check To username against peer certificate CN.
+ Returns 1 on success, negative on failure.
+
+ This function can be used from REQUEST_ROUTE.
+
+ Example 1.35. tls_check_to usage
+...
+# Authenticate for REGISTER
+if (proto==TLS && is_peer_verified()){
+ if (!tls_check_to()){
+ xlog("L_NOTICE","[$pr:$fU@$si:$sp]: Processing '$rm' auth TO check failed\n");
+ sl_send_reply("403","Forbidden auth ID");
+ exit;
+ }
+}
+...
+
+1.5.3. tls_check_from
+
+ Check From username against peer certificate CN.
+ Returns 1 on success, negative on failure.
+
+ This function can be used from REQUEST_ROUTE.
+
+ Example 1.35. is_peer_verified usage
+...
+# Authenticate for MESSAGE / INVITE
+if (proto==TLS && tls_check_from()){
+ if (!tls_check_from()){
+ xlog("L_NOTICE","[$pr:$fU@$si:$sp]: Processing '$rm' auth FROM check failed\n");
+ sl_send_reply("403","Forbidden auth ID");
+ exit;
+ }
+}
+...
+
1.6. Exported MI Functions
1.6.1. tls_list
diff --git a/modules/tls_mgm/doc/tls_mgm_admin.xml b/modules/tls_mgm/doc/tls_mgm_admin.xml
index 463b03ce44e..91d082cc721 100644
--- a/modules/tls_mgm/doc/tls_mgm_admin.xml
+++ b/modules/tls_mgm/doc/tls_mgm_admin.xml
@@ -111,6 +111,60 @@ if (is_peer_verified()) {
xlog("L_INFO","request not verified\n");
}
...
+
+
+
+
+
+ tls_check_to
+
+
+ Check To username against peer certificate CN.
+ Returns 1 on success, negative on failure.
+
+
+ This function can be used from REQUEST_ROUTE.
+
+
+ tls_check_to usage
+
+...
+# Authenticate for REGISTER
+if (proto==TLS && is_peer_verified()){
+ if (!tls_check_to()){
+ xlog("L_NOTICE","[$pr:$fU@$si:$sp]: Processing '$rm' auth TO check failed\n");
+ sl_send_reply("403","Forbidden auth ID");
+ exit;
+ }
+}
+...
+
+
+
+
+
+ tls_check_from
+
+
+ Check From username against peer certificate CN.
+ Returns 1 on success, negative on failure.
+
+
+ This function can be used from REQUEST_ROUTE.
+
+
+ tls_check_from usage
+
+...
+# Authenticate for MESSAGE / INVITE
+if (proto==TLS && tls_check_from()){
+ if (!tls_check_from()){
+ xlog("L_NOTICE","[$pr:$fU@$si:$sp]: Processing '$rm' auth FROM check failed\n");
+ sl_send_reply("403","Forbidden auth ID");
+ exit;
+ }
+}
+...
diff --git a/modules/tls_mgm/tls_mgm.c b/modules/tls_mgm/tls_mgm.c
index bd8724ea906..3a9187b9b39 100644
--- a/modules/tls_mgm/tls_mgm.c
+++ b/modules/tls_mgm/tls_mgm.c
@@ -34,6 +34,7 @@
#include "tls_select.h"
#include "tls.h"
#include "api.h"
+#include "../../parser/parse_from.h"
#define DB_CAP DB_CAP_QUERY | DB_CAP_UPDATE
#define len(s) s == NULL?0:strlen(s)
@@ -77,6 +78,11 @@ int tls_db_enabled = 0;
/* definition of exported functions */
static int is_peer_verified(struct sip_msg*, char*, char*);
+static int tls_check_to(struct sip_msg*, char*, char*);
+static int tls_check_from(struct sip_msg*, char*, char*);
+#define ERR_USERNOTFOUND -4 /* No found username error */
+#define ERR_SPOOFEDUSER -9 /* Spoofed User Error */
+#define ERR_NOMATCH -10 /* No match Error */
static param_export_t params[] = {
{ "client_domain_avp", STR_PARAM, &tls_domain_avp },
@@ -119,6 +125,10 @@ static param_export_t params[] = {
static cmd_export_t cmds[] = {
{"is_peer_verified", (cmd_function)is_peer_verified, 0, 0, 0,
REQUEST_ROUTE},
+ {"tls_check_to", (cmd_function)tls_check_to, 0, 0, 0,
+ REQUEST_ROUTE},
+ {"tls_check_from", (cmd_function)tls_check_from, 0, 0, 0,
+ REQUEST_ROUTE},
{"load_tls_mgm", (cmd_function)load_tls_mgm, 0, 0, 0, 0},
{0,0,0,0,0,0}
};
@@ -1468,6 +1478,118 @@ static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2)
return -1;
}
+#define USERNAME_BUFF_SIZE 256
+
+/*
+ * Check if the provided username matches certificate CN.
+ */
+static int tls_check_username(struct sip_msg* _m, const str *usr) {
+ str cn = {0,0};
+ char cn_buff[USERNAME_BUFF_SIZE];
+
+ /* Get CN from the peer certificate */
+ if (tlsops_get_peer_cn(_m, &cn, cn_buff, USERNAME_BUFF_SIZE) != 0) {
+ LM_ERR("Could not extract CN\n");
+ return -1;
+ }
+
+ /* Check URI match to the CN match */
+ if (usr->len == cn.len) {
+ if (!strncasecmp(usr->s, cn.s, (size_t)usr->len)) {
+ LM_DBG("Digest username and URI username match\n");
+ return 1;
+
+ } else {
+ LM_INFO("Spoofed user '%.*s' should be '%.*s' as in CN\n",
+ usr->len, usr->s,
+ cn.len, cn.s);
+ return ERR_SPOOFEDUSER;
+ }
+ }
+
+ LM_INFO("Digest username and URI username do NOT match, '%.*s' should be '%.*s' as in CN\n",
+ usr->len, usr->s,
+ cn.len, cn.s);
+ return ERR_NOMATCH;
+}
+
+/*
+ * Check if the provided uri matches peer CN.
+ * user@hostname is taken from the URI.
+ */
+static int tls_check_username_uri(struct sip_msg* _m, struct sip_uri *_uri) {
+ str usr = {0,0};
+ char usr_buff[USERNAME_BUFF_SIZE];
+
+ if (_uri == NULL) {
+ LM_ERR("Bad parameter\n");
+ return -1;
+ }
+
+ /* Parse To/From URI */
+ /* Make sure that the URI contains username */
+ if (_uri->user.len == 0 || _uri->host.len == 0) {
+ LM_ERR("Username not found in URI\n");
+ return ERR_USERNOTFOUND;
+ }
+
+ /* make CN like identifier */
+ if ((_uri->user.len + _uri->host.len + 4) >= USERNAME_BUFF_SIZE){
+ LM_ERR("Username buffer too short\n");
+ return -1;
+ }
+
+ snprintf(usr_buff, USERNAME_BUFF_SIZE, "%.*s@%.*s",
+ _uri->user.len, _uri->user.s,
+ _uri->host.len, _uri->host.s);
+ usr.s = usr_buff;
+ usr.len = _uri->user.len + _uri->host.len + 1;
+
+ return tls_check_username(_m, &usr);
+}
+
+/*
+ * Check username part in To header field matches peer certificate Common Name (CN).
+ */
+static int tls_check_to(struct sip_msg* _m, char* _s1, char* _s2)
+{
+ if (_m->rcv.proto != PROTO_TLS) {
+ LM_ERR("proto != TLS --> peer can't be verified, return -1\n");
+ return -1;
+ }
+ if (!_m->to && ((parse_headers(_m, HDR_TO_F, 0) == -1) || (!_m->to))) {
+ LM_ERR("Error while parsing To header field\n");
+ return -1;
+ }
+ if(parse_to_uri(_m)==NULL) {
+ LM_ERR("Error while parsing To header URI\n");
+ return -1;
+ }
+
+ return tls_check_username_uri(_m, &get_to(_m)->parsed_uri);
+}
+
+/*
+ * Check username part in From header field matches peer certificate Common Name (CN).
+ */
+static int tls_check_from(struct sip_msg* _m, char* _s1, char* _s2)
+{
+ if (_m->rcv.proto != PROTO_TLS) {
+ LM_ERR("proto != TLS --> peer can't be verified, return -1\n");
+ return -1;
+ }
+ if (parse_from_header(_m) < 0) {
+ LM_ERR("Error while parsing From header field\n");
+ return -1;
+ }
+ if(parse_from_uri(_m)==NULL) {
+ LM_ERR("Error while parsing From header URI\n");
+ return -1;
+ }
+
+ return tls_check_username_uri(_m, &get_from(_m)->parsed_uri);
+}
+
static int tls_get_handshake_timeout(void)
{
return tls_handshake_timeout;
diff --git a/modules/tls_mgm/tls_select.c b/modules/tls_mgm/tls_select.c
index 6f918fd7b50..3e94d195428 100644
--- a/modules/tls_mgm/tls_select.c
+++ b/modules/tls_mgm/tls_select.c
@@ -414,6 +414,55 @@ int tlsops_sn(struct sip_msg *msg, pv_param_t *param,
return 0;
}
+int tlsops_get_peer_cn(struct sip_msg *msg, str *res, char * buff, size_t buff_size)
+{
+ X509* cert;
+ struct tcp_connection* c;
+ X509_NAME* name;
+ X509_NAME_ENTRY* e;
+ ASN1_STRING* asn1;
+ int index;
+ str text;
+
+ res->s = 0;
+ res->len = 0;
+ text.s = 0;
+ if (get_cert(&cert, &c, msg, 0) < 0) {
+ return -1;
+ }
+
+ name = X509_get_subject_name(cert);
+ if (!name) {
+ LM_ERR("cannot extract subject or issuer name from peer"
+ " certificate\n");
+ goto err;
+ }
+
+ index = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+ e = X509_NAME_get_entry(name, index);
+ asn1 = X509_NAME_ENTRY_get_data(e);
+ text.len = ASN1_STRING_to_UTF8((unsigned char**)(void*)&text.s, asn1);
+ if (text.len < 0 || text.len >= buff_size) {
+ LM_ERR("failed to convert ASN1 string\n");
+ goto err;
+ }
+ memcpy(buff, text.s, text.len >= buff_size ? buff_size : (size_t)text.len);
+ res->s = buff;
+ res->len = text.len;
+
+ OPENSSL_free(text.s);
+
+ X509_free(cert);
+ tcp_conn_release(c,0);
+ return 0;
+
+err:
+ if (text.s) OPENSSL_free(text.s);
+ X509_free(cert);
+ tcp_conn_release(c,0);
+ return -2;
+}
+
int tlsops_comp(struct sip_msg *msg, pv_param_t *param,
pv_value_t *res)
{
diff --git a/modules/tls_mgm/tls_select.h b/modules/tls_mgm/tls_select.h
index 290581dbaea..c79be12278f 100644
--- a/modules/tls_mgm/tls_select.h
+++ b/modules/tls_mgm/tls_select.h
@@ -97,6 +97,9 @@ int tlsops_validity(struct sip_msg *msg, pv_param_t *param,
int tlsops_sn(struct sip_msg *msg, pv_param_t *param,
pv_value_t *res);
+int tlsops_get_peer_cn(struct sip_msg *msg, str *res, char * buff,
+ size_t buff_size);
+
int tlsops_comp(struct sip_msg *msg, pv_param_t *param,
pv_value_t *res);