From 142711b6033963bc91d77a49d9aa4c514d5c0f64 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 27 Nov 2015 00:28:45 +0100 Subject: [PATCH 1/2] 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/2] 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