From 142711b6033963bc91d77a49d9aa4c514d5c0f64 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 27 Nov 2015 00:28:45 +0100 Subject: [PATCH 1/4] CRL: tls_crl_directory configuration option ported from OpenSips 2.x branch --- cfg.lex | 3 + cfg.y | 32 ++++++++++- config.h | 1 + tls/tls_config.c | 2 + tls/tls_config.h | 2 + tls/tls_domain.c | 1 + tls/tls_domain.h | 2 + tls/tls_init.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 181 insertions(+), 2 deletions(-) diff --git a/cfg.lex b/cfg.lex index 3f69baf37ba..d1de98af8ea 100644 --- a/cfg.lex +++ b/cfg.lex @@ -357,6 +357,7 @@ TLS_CERTIFICATE "tls_certificate" TLS_PRIVATE_KEY "tls_private_key" TLS_CA_LIST "tls_ca_list" TLS_CA_DIR "tls_ca_dir" +TLS_CRL_DIR "tls_crl_dir" TLS_DH_PARAMS "tls_dh_params" TLS_EC_CURVE "tls_ec_curve" TLS_CIPHERS_LIST "tls_ciphers_list" @@ -678,6 +679,8 @@ IMPORTFILE "import_file" return TLS_CA_LIST; } {TLS_CA_DIR} { count(); yylval.strval=yytext; return TLS_CA_DIR; } +{TLS_CRL_DIR} { count(); yylval.strval=yytext; + return TLS_CRL_DIR; } {TLS_DH_PARAMS} { count(); yylval.strval=yytext; return TLS_DH_PARAMS; } {TLS_EC_CURVE} { count(); yylval.strval=yytext; diff --git a/cfg.y b/cfg.y index b969ce08824..071ffda97eb 100644 --- a/cfg.y +++ b/cfg.y @@ -412,7 +412,8 @@ extern char *finame; %token TLS_CERTIFICATE %token TLS_PRIVATE_KEY %token TLS_CA_LIST -%token TLS_CA_DIR +%token TLS_CA_DIR +%token TLS_CRL_DIR %token TLS_CIPHERS_LIST %token TLS_DH_PARAMS %token TLS_EC_CURVE @@ -1172,7 +1173,18 @@ assign_stm: DEBUG EQUAL snumber { #endif } | TLS_CA_DIR EQUAL error { yyerror("string value expected"); } - | TLS_CIPHERS_LIST EQUAL STRING { + | TLS_CRL_DIR EQUAL STRING { + #ifdef USE_TLS + tls_default_server_domain->crl_directory = + $3; + tls_default_client_domain->crl_directory = + $3; + #else + warn("tls support not compiled in"); + #endif + } + | TLS_CRL_DIR EQUAL error { yyerror("string value expected"); } + | TLS_CIPHERS_LIST EQUAL STRING { #ifdef USE_TLS tls_default_server_domain->ciphers_list = $3; @@ -1652,6 +1664,14 @@ tls_server_var : TLS_METHOD EQUAL SSLv23 { #endif } | TLS_CA_DIR EQUAL error { yyerror("string value expected"); } + | TLS_CRL_DIR EQUAL STRING { + #ifdef USE_TLS + tls_server_domains->crl_directory=$3; + #else + warn("tls support not compiled in"); + #endif + } + | TLS_CRL_DIR EQUAL error { yyerror("string value expected"); } | TLS_CIPHERS_LIST EQUAL STRING { #ifdef USE_TLS tls_server_domains->ciphers_list=$3; @@ -1766,6 +1786,14 @@ tls_client_var : TLS_METHOD EQUAL SSLv23 { #endif } | TLS_CA_DIR EQUAL error { yyerror("string value expected"); } + | TLS_CRL_DIR EQUAL STRING { + #ifdef USE_TLS + tls_client_domains->crl_directory=$3; + #else + warn("tls support not compiled in"); + #endif + } + | TLS_CRL_DIR EQUAL error { yyerror("string value expected"); } | TLS_CIPHERS_LIST EQUAL STRING { #ifdef USE_TLS tls_client_domains->ciphers_list=$3; diff --git a/config.h b/config.h index 65743673e0d..1f8b61f4ffc 100644 --- a/config.h +++ b/config.h @@ -48,6 +48,7 @@ #define TLS_CERT_FILE CFG_DIR "tls/cert.pem" #define TLS_CA_FILE 0 /*!< no CA list file by default */ #define TLS_CA_DIRECTORY "/etc/pki/CA/" +#define TLS_CRL_DIRECTORY NULL #define TLS_DH_PARAMS_FILE 0 /*!< no DH params file by default */ #define MAX_LISTEN 16 /*!< maximum number of addresses on which we will listen */ diff --git a/tls/tls_config.c b/tls/tls_config.c index ef98dd985c3..46c29ce502c 100644 --- a/tls/tls_config.c +++ b/tls/tls_config.c @@ -39,9 +39,11 @@ int tls_method = TLS_USE_SSLv23; int tls_verify_client_cert = 1; int tls_verify_server_cert = 1; int tls_require_client_cert = 1; +int crl_check_all = 0; /* default location of certificates */ char *tls_cert_file = TLS_CERT_FILE; char *tls_pkey_file = TLS_PKEY_FILE; +char *tls_crl_directory = NULL; char *tls_ca_file = TLS_CA_FILE; char *tls_ca_dir = TLS_CA_DIRECTORY; char *tls_tmp_dh_file = TLS_DH_PARAMS_FILE; diff --git a/tls/tls_config.h b/tls/tls_config.h index c05672bf133..c130e0c19ad 100644 --- a/tls/tls_config.h +++ b/tls/tls_config.h @@ -47,12 +47,14 @@ enum tls_method { extern int tls_log; extern int tls_method; +extern int crl_check_all; extern int tls_verify_client_cert; extern int tls_verify_server_cert; extern int tls_require_client_cert; extern char *tls_cert_file; extern char *tls_pkey_file; +extern char *tls_crl_directory; extern char *tls_ca_file; extern char *tls_ca_dir; extern char *tls_tmp_dh_file; diff --git a/tls/tls_domain.c b/tls/tls_domain.c index ef05e0fc5af..7b35bb7ad82 100644 --- a/tls/tls_domain.c +++ b/tls/tls_domain.c @@ -185,6 +185,7 @@ struct tls_domain *tls_new_domain(int type) memset( d, 0, sizeof(struct tls_domain)); d->type = type; + d->crl_check_all = crl_check_all; if (type & TLS_DOMAIN_SRV) { d->verify_cert = tls_verify_client_cert; diff --git a/tls/tls_domain.h b/tls/tls_domain.h index 339e74ded2b..1f9cdb8f10b 100644 --- a/tls/tls_domain.h +++ b/tls/tls_domain.h @@ -51,8 +51,10 @@ struct tls_domain { SSL_CTX *ctx; int verify_cert; int require_client_cert; + int crl_check_all; char *cert_file; char *pkey_file; + char *crl_directory; char *ca_file; char *tmp_dh_file; char *tls_ec_curve; diff --git a/tls/tls_init.c b/tls/tls_init.c index d2cec3b70bf..4ccb7db8089 100644 --- a/tls/tls_init.c +++ b/tls/tls_init.c @@ -42,6 +42,7 @@ #include #include #include +#include #define OS_SSL_SESS_ID ((unsigned char*)"opensips-tls-1.11.0") #define OS_SSL_SESS_ID_LEN (sizeof(OS_SSL_SESS_ID)-1) @@ -260,6 +261,135 @@ load_certificate(SSL_CTX * ctx, char *filename) return 0; } +static int +tls_load_crl_to_store(X509_STORE *store, char *crl_directory, int *p_crl_added) +{ + DIR *d; + struct dirent *dir; + int crl_added = 0; + + if(!store) { + LM_ERR("Unable to get X509 store from ssl context\n"); + return -1; + } + + /*Parse directory*/ + d = opendir(crl_directory); + if(!d) { + LM_ERR("Unable to open crl directory '%s'\n", crl_directory); + return -1; + } + + while ((dir = readdir(d)) != NULL) { + /*Skip if not regular file*/ + if (dir->d_type != DT_REG) + continue; + + /*Create filename*/ + char* filename = (char*) pkg_malloc(sizeof(char)*(strlen(crl_directory)+strlen(dir->d_name)+2)); + if (!filename) { + LM_ERR("Unable to allocate crl filename\n"); + closedir(d); + return -1; + } + strcpy(filename,crl_directory); + if(filename[strlen(filename)-1] != '/') + strcat(filename,"/"); + strcat(filename,dir->d_name); + + /*Get CRL content*/ + FILE *fp = fopen(filename,"r"); + pkg_free(filename); + if(!fp) + continue; + + X509_CRL *crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL); + fclose(fp); + if(!crl) + continue; + + /*Add CRL to X509 store*/ + if (X509_STORE_add_crl(store, crl) == 1) + crl_added++; + else + LM_ERR("Unable to add crl to ssl context\n"); + + X509_CRL_free(crl); + } + closedir(d); + + if (p_crl_added != NULL){ + *p_crl_added = crl_added; + } + + if (!crl_added) { + LM_ERR("No suitable CRL files found in directory %s\n", crl_directory); + return 0; + } else { + LM_INFO("CRLs added: %d\n", crl_added); + } + + return 0; +} + +static int +tls_set_crl_checking(SSL_CTX * ctx, int enable, int crl_check_all) +{ + X509_VERIFY_PARAM *param; + if (ctx == NULL){ + LM_ERR("Passed SSL context is null"); + return -1; + } + + X509_VERIFY_PARAM *orig_param = ctx->param; + param = X509_VERIFY_PARAM_new(); + if (param == NULL){ + LM_ERR("Could not init a new param"); + return -1; + } + + unsigned long flags = orig_param != NULL ? X509_VERIFY_PARAM_get_flags(orig_param) : 0ul; + if (enable) + flags |= X509_V_FLAG_CRL_CHECK; + else + flags &= ~X509_V_FLAG_CRL_CHECK; + if(enable && crl_check_all) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + + X509_VERIFY_PARAM_set_flags(param, flags); + + SSL_CTX_set1_param(ctx, param); + X509_VERIFY_PARAM_free(param); + return 0; +} + +static int +load_crl(SSL_CTX * ctx, char *crl_directory, int crl_check_all) +{ + int crl_added = 0; + LM_DBG("Loading CRL from directory\n"); + + /*Get X509 store from SSL context*/ + X509_STORE *store = SSL_CTX_get_cert_store(ctx); + if(!store) { + LM_ERR("Unable to get X509 store from ssl context\n"); + return -1; + } + + /*Load CRL to provided store*/ + if (tls_load_crl_to_store(store, crl_directory, &crl_added) != 0){ + LM_ERR("Could not load CRL"); + return -1; + } + + if (!crl_added) { + LM_ERR("No suitable CRL files found in directory %s\n", crl_directory); + } + + /*Enable CRL checking*/ + tls_set_crl_checking(ctx, crl_added > 0, crl_check_all); + return 0; +} #define NUM_RETRIES 3 /* @@ -833,6 +963,16 @@ init_tls_domains(struct tls_domain *d) if (load_certificate(d->ctx, d->cert_file) < 0) return -1; + /** + * load crl from directory + */ + if (!d->crl_directory) { + LM_NOTICE("no crl for tls, using none"); + } else { + if(load_crl(d->ctx, d->crl_directory, d->crl_check_all) < 0) + return -1; + } + /* * load ca */ From 5c0c7e8e30ab2da86d209cc1daa7e2d65425ca7a Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 27 Nov 2015 00:32:56 +0100 Subject: [PATCH 2/4] CRL-CN: CRL refresh via MI command added --- modules/tlsops/tlsops.c | 28 +++++++- tls/tls_init.c | 145 ++++++++++++++++++++++++++++++++++++++++ tls/tls_init.h | 10 +++ 3 files changed, 181 insertions(+), 2 deletions(-) diff --git a/modules/tlsops/tlsops.c b/modules/tlsops/tlsops.c index e39fd29b2f1..833e95e9d4f 100644 --- a/modules/tlsops/tlsops.c +++ b/modules/tlsops/tlsops.c @@ -43,7 +43,7 @@ #include "../../tcp_server.h" /* tcpconn_get() */ #include "../../sr_module.h" #include "../../pvar.h" - +#include "../../tls/tls_init.h" int tcp_con_lifetime=DEFAULT_TCP_CONNECTION_LIFETIME; @@ -51,6 +51,9 @@ int tcp_con_lifetime=DEFAULT_TCP_CONNECTION_LIFETIME; /* definition of exported functions */ static int is_peer_verified(struct sip_msg*, char*, char*); +/* MI function to refresh all TLS CRL & CA configuration */ +static struct mi_root* mi_refresh_crl_ca(struct mi_root* cmd, void* param); + /* definition of internal functions */ static int mod_init(void); static void mod_destroy(void); @@ -75,6 +78,14 @@ static param_export_t params[] = { {0,0,0} }; +/* + * MI Commands + */ +static mi_export_t mi_cmds[] = { + { "refresh_crl_ca", 0, mi_refresh_crl_ca, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0} +}; + /* * pseudo variables */ @@ -249,7 +260,7 @@ struct module_exports exports = { cmds, /* Exported functions */ params, /* Exported parameters */ 0, /* exported statistics */ - 0, /* exported MI functions */ + mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ @@ -334,3 +345,16 @@ static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2) "...done\n"); return 1; } + +/* + * mi cmd: refresh_crl_ca + * + */ + +static struct mi_root* mi_refresh_crl_ca(struct mi_root* cmd, void* param) +{ + LM_INFO("mi_refresh_crl_ca:start\n"); + reload_tls_domains_crl_ca_all(); + + return init_mi_tree(200, MI_OK_S, MI_OK_LEN); +} \ No newline at end of file diff --git a/tls/tls_init.c b/tls/tls_init.c index 4ccb7db8089..b9ff11306b7 100644 --- a/tls/tls_init.c +++ b/tls/tls_init.c @@ -450,6 +450,23 @@ load_ca(SSL_CTX * ctx, char *filename) return 0; } +/* + * Load a caList, to be used to verify the client's certificate. + * The list is to be stored in a single file, containing all + * the acceptable root certificates. + */ +static int +load_ca_to_store(X509_STORE *store, char *filename) +{ + LM_DBG("Entered\n"); + if (!X509_STORE_load_locations(store, filename, 0)) { + LM_ERR("unable to load ca '%s'\n", filename); + return -1; + } + + LM_DBG("CA '%s' successfuly loaded\n", filename); + return 0; +} /* * Load a caList from a directory instead of a single file. @@ -467,6 +484,21 @@ load_ca_dir(SSL_CTX * ctx, char *directory) return 0; } +/* + * Load a caList from a directory instead of a single file. + */ +static int +load_ca_dir_to_store(X509_STORE * store, char *directory) +{ + LM_DBG("Entered\n"); + if (!X509_STORE_load_locations(store, 0 , directory)) { + LM_ERR("unable to load ca directory '%s'\n", directory); + return -1; + } + + LM_DBG("CA '%s' successfuly loaded from directory\n", directory); + return 0; +} #if (OPENSSL_VERSION_NUMBER > 0x10001000L) /* @@ -1019,6 +1051,119 @@ init_tls_domains(struct tls_domain *d) return 0; } +int +reload_tls_domains_crl_ca(struct tls_domain *d) +{ + struct tls_domain *dom; + + dom = d; + while (d) { + if (d->name.len) { + LM_INFO("Reloading TLS domain '%.*s'\n", + d->name.len, ZSW(d->name.s)); + } else { + LM_INFO("Reloading TLS domain [%s:%d]\n", + ip_addr2a(&d->addr), d->port); + } + + int crl_count = 0; + int crl_load_ok = 0; + + /* + * Create a new cert store. + */ + X509_STORE * new_store = X509_STORE_new(); + if (new_store == NULL){ + LM_ERR("Could not allocate new X509 store"); + return -1; + } + + /** + * load crl from directory + */ + if (!d->crl_directory) { + LM_NOTICE("no crl for tls, using none"); + } else { + if (tls_load_crl_to_store(new_store, d->crl_directory, &crl_count) < 0){ + return -1; + } + crl_load_ok = 1; + } + + /* + * load ca + */ + if (!d->ca_file) { + LM_NOTICE("no CA for tls[%s:%d] defined, " + "using default '%s'\n", ip_addr2a(&d->addr), d->port, + tls_ca_file); + d->ca_file = tls_ca_file; + } + if (d->ca_file && load_ca_to_store(new_store, d->ca_file) < 0) { + return -1; + } + + /* + * load ca from directory + */ + if (!d->ca_directory) { + + LM_NOTICE("no CA for tls[%s:%d] defined, " + "using default '%s'\n", ip_addr2a(&d->addr), d->port, + tls_ca_dir); + d->ca_directory = tls_ca_dir; + } + + if (d->ca_directory && load_ca_dir_to_store(new_store, d->ca_directory) < 0) { + return -1; + } + + /* + * set new store - critical place, here we replace the old store with the new one. + * Locking mechanism may be needed to protect from race conditions. + */ + SSL_CTX_set_cert_store(d->ctx, new_store); + + /* + * CRL checking enable / disable + */ + tls_set_crl_checking(d->ctx, crl_load_ok>0 && crl_count>0, d->crl_check_all); + + d = d->next; + } + + return 0; +} + +int +reload_tls_domains_crl_ca_all(void) +{ + int i; + + /* + * now reinitialize tls default domains + */ + if ( (i=reload_tls_domains_crl_ca(tls_default_server_domain)) ) { + return i; + } + if ( (i=reload_tls_domains_crl_ca(tls_default_client_domain)) ) { + return i; + } + /* + * now reinitialize tls virtual domains + */ + if ( (i=reload_tls_domains_crl_ca(tls_server_domains)) ) { + return i; + } + if ( (i=reload_tls_domains_crl_ca(tls_client_domains)) ) { + return i; + } + /* + * we are all set + */ + return 0; +} + /* * called from main.c when opensips exits (main process) */ diff --git a/tls/tls_init.h b/tls/tls_init.h index 91c7874edf7..98a7b538796 100644 --- a/tls/tls_init.h +++ b/tls/tls_init.h @@ -56,4 +56,14 @@ int init_tls(void); */ int init_tls_domains(struct tls_domain *d); +/* + * reloads TLS configuration for given domain: CRL, CA list. + */ +int reload_tls_domains_crl_ca(struct tls_domain *d); + +/* + * reloads TLS configuration for all domains: CRL, CA list. + */ +int reload_tls_domains_crl_ca_all(void); + #endif From 64d5aff6e43f8852299cc5432bac5ac28f75a996 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 27 Nov 2015 00:35:27 +0100 Subject: [PATCH 3/4] CN-AUTH: authentication via TLS client certificate functions added to tlsops module --- modules/tlsops/tls_select.c | 51 ++++++++++++++ modules/tlsops/tls_select.h | 5 +- modules/tlsops/tlsops.c | 134 +++++++++++++++++++++++++++++++++++- 3 files changed, 188 insertions(+), 2 deletions(-) diff --git a/modules/tlsops/tls_select.c b/modules/tlsops/tls_select.c index 0b00a1a8119..d0386007cc2 100644 --- a/modules/tlsops/tls_select.c +++ b/modules/tlsops/tls_select.c @@ -404,6 +404,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, my = 0; + str text; + + res->s = 0; + res->len = 0; + text.s = 0; + if (get_cert(&cert, &c, msg, my) < 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 >= 1024) { + 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); + + if (!my) X509_free(cert); + tcpconn_put(c); + return 0; + +err: + if (text.s) OPENSSL_free(text.s); + if (!my) X509_free(cert); + tcpconn_put(c); + return -2; +} + int tlsops_comp(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { @@ -451,6 +500,7 @@ int tlsops_comp(struct sip_msg *msg, pv_param_t *param, case COMP_C: nid = NID_countryName; break; case COMP_ST: nid = NID_stateOrProvinceName; break; case COMP_L: nid = NID_localityName; break; + case COMP_SUBJECT_SERIAL: nid = NID_serialNumber;break; default: nid = NID_undef; } @@ -478,6 +528,7 @@ int tlsops_comp(struct sip_msg *msg, pv_param_t *param, case COMP_C: elem = "CountryName"; break; case COMP_ST: elem = "StateOrProvinceName"; break; case COMP_L: elem = "LocalityName"; break; + case COMP_SUBJECT_SERIAL: elem = "SubjectSerialNumber";break; default: elem = "Unknown"; break; } LM_DBG("element %s not found in " diff --git a/modules/tlsops/tls_select.h b/modules/tlsops/tls_select.h index 0339dc6d8fe..a7e01eae079 100644 --- a/modules/tlsops/tls_select.h +++ b/modules/tlsops/tls_select.h @@ -51,7 +51,8 @@ enum { COMP_HOST = 1<<16, /* hostname from subject/alternative */ COMP_URI = 1<<17, /* URI from subject/alternative */ COMP_E = 1<<18, /* Email address */ - COMP_IP = 1<<19 /* IP from subject/alternative */ + COMP_IP = 1<<19, /* IP from subject/alternative */ + COMP_SUBJECT_SERIAL = 1<<20 /*Serial name from Subject*/ }; @@ -91,4 +92,6 @@ int tlsops_comp(struct sip_msg *msg, pv_param_t *param, int tlsops_alt(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); #endif diff --git a/modules/tlsops/tlsops.c b/modules/tlsops/tlsops.c index 833e95e9d4f..522944a54ab 100644 --- a/modules/tlsops/tlsops.c +++ b/modules/tlsops/tlsops.c @@ -44,6 +44,8 @@ #include "../../sr_module.h" #include "../../pvar.h" #include "../../tls/tls_init.h" +#include "../../parser/parse_from.h" +#include "../../parser/digest/digest.h" int tcp_con_lifetime=DEFAULT_TCP_CONNECTION_LIFETIME; @@ -51,6 +53,18 @@ int tcp_con_lifetime=DEFAULT_TCP_CONNECTION_LIFETIME; /* definition of exported functions */ static int is_peer_verified(struct sip_msg*, char*, char*); +/* + * Check if To header field contains the same username + * as digest credentials + */ +int tls_check_to(struct sip_msg* _msg, char* _str1, char* _str2); + +/* + * Check if From header field contains the same username + * as digest credentials + */ +int tls_check_from(struct sip_msg* _msg, char* _str1, char* _str2); + /* MI function to refresh all TLS CRL & CA configuration */ static struct mi_root* mi_refresh_crl_ca(struct mi_root* cmd, void* param); @@ -58,6 +72,19 @@ static struct mi_root* mi_refresh_crl_ca(struct mi_root* cmd, void* param); static int mod_init(void); static void mod_destroy(void); +/* Return codes reference */ +#define OK 1 /* success */ +#define ERR_INTERNAL -1 /* Internal Error */ +#define ERR_CREDENTIALS -2 /* No credentials error */ +#define ERR_DBUSE -3 /* Data Base Use error */ +#define ERR_USERNOTFOUND -4 /* No found username error */ +#define ERR_DBEMTPYRES -5 /* Emtpy Query Result */ + +#define ERR_DBACCESS -7 /* Data Base Access Error */ +#define ERR_DBQUERY -8 /* Data Base Query Error */ +#define ERR_SPOOFEDUSER -9 /* Spoofed User Error */ +#define ERR_NOMATCH -10 /* No match Error */ + /* * Module parameter variables */ @@ -68,6 +95,10 @@ static void mod_destroy(void); 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}, {0,0,0,0,0,0} }; @@ -192,6 +223,12 @@ static pv_export_t mod_items[] = { {{"tls_peer_subject_unit", sizeof("tls_peer_subject_unit")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_OU }, + {{"tls_my_subject_serial", sizeof("tls_my_subject_serial")-1}, + 850, tlsops_comp, 0, + 0, 0, pv_init_iname, CERT_LOCAL | CERT_SUBJECT | COMP_SUBJECT_SERIAL }, + {{"tls_peer_subject_serial", sizeof("tls_peer_subject_serial")-1}, + 850, tlsops_comp, 0, + 0, 0, pv_init_iname, CERT_PEER | CERT_SUBJECT | COMP_SUBJECT_SERIAL }, {{"tls_peer_issuer_unit", sizeof("tls_peer_issuer_unit")-1}, 850, tlsops_comp, 0, 0, 0, pv_init_iname, CERT_PEER | CERT_ISSUER | COMP_OU }, @@ -357,4 +394,99 @@ static struct mi_root* mi_refresh_crl_ca(struct mi_root* cmd, void* param) reload_tls_domains_crl_ca_all(); return init_mi_tree(200, MI_OK_S, MI_OK_LEN); -} \ No newline at end of file +} + +/* + * Check if a header field contains the same username + * as TLS CN + */ +static inline int check_username(struct sip_msg* _m, struct sip_uri *_uri) { +#define CN_BUFF_SIZE 256 + str cn = {0,0}; + str usr = {0,0}; + char cn_buff[CN_BUFF_SIZE]; + char usr_buff[CN_BUFF_SIZE]; + + if (_uri == NULL) { + LM_ERR("Bad parameter\n"); + return ERR_INTERNAL; + } + + /* 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) >= CN_BUFF_SIZE){ + LM_ERR("Username buffer too short\n"); + return ERR_INTERNAL; + } + + snprintf(usr_buff, CN_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; + + /* Get CN from the peer certificate */ + if (tlsops_get_peer_cn(_m, &cn, cn_buff, CN_BUFF_SIZE) != 0) { + LM_ERR("Could not extract CN\n"); + return ERR_INTERNAL; + } + + /* Check URI match to the CN match */ + if (usr.len == cn.len) { + if (!strncasecmp(usr.s, cn.s, + usr.len)) { + LM_DBG("Digest username and URI username match\n"); + return OK; + } 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 username part in To header field + */ +int tls_check_to(struct sip_msg* _m, char* _s1, char* _s2) +{ + if (!_m->to && ((parse_headers(_m, HDR_TO_F, 0) == -1) || (!_m->to))) { + LM_ERR("Error while parsing To header field\n"); + return ERR_INTERNAL; + } + if(parse_to_uri(_m)==NULL) { + LM_ERR("Error while parsing To header URI\n"); + return ERR_INTERNAL; + } + + return check_username(_m, &get_to(_m)->parsed_uri); +} + +/* + * Check username part in From header field + */ +int tls_check_from(struct sip_msg* _m, char* _s1, char* _s2) +{ + if (parse_from_header(_m) < 0) { + LM_ERR("Error while parsing From header field\n"); + return ERR_INTERNAL; + } + if(parse_from_uri(_m)==NULL) { + LM_ERR("Error while parsing From header URI\n"); + return ERR_INTERNAL; + } + + return check_username(_m, &get_from(_m)->parsed_uri); +} From d6ae9b2bdee0c8fdac29653519fff62dc657c312 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 27 Nov 2015 10:55:34 +0100 Subject: [PATCH 4/4] CN-AUTH: fixed buffer size check in peer CN extraction --- modules/tlsops/tls_select.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tlsops/tls_select.c b/modules/tlsops/tls_select.c index d0386007cc2..ecb8462db9d 100644 --- a/modules/tlsops/tls_select.c +++ b/modules/tlsops/tls_select.c @@ -432,7 +432,7 @@ int tlsops_get_peer_cn(struct sip_msg *msg, str *res, char * buff, size_t buff_s 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 >= 1024) { + if (text.len < 0 || text.len >= buff_size) { LM_ERR("failed to convert ASN1 string\n"); goto err; }