#include "ssl_trusted_cert_storage.h" #include "MESA_htable_aux.h" #include #include #include #include #include #include #include #include int SSL_EX_DATA_IDX_VERIFY_PARAM=-1; struct ssl_X509_object { char* filename; enum ssl_X509_obj_type type; }; static void free_ssl_x509_obj(void* data) { struct ssl_X509_object* obj=(struct ssl_X509_object*)data; free(obj->filename); free(obj); return; } struct ssl_trusted_cert_storage { struct cert_store_param param; char* pem_bundle, *pem_dir; MESA_htable_handle hash_table; pthread_rwlock_t rwlock; X509_STORE* effective_store; }; static int _X509_add_cert_or_crl_add(X509_STORE* store, enum ssl_X509_obj_type type, const char* filename) { int ret=0; BIO *bio=NULL; X509* x=NULL; X509_CRL* x_crl=NULL; bio=BIO_new_file(filename, "r"); if(bio==NULL) { return -1; } ret=0; if(type==SSL_X509_OBJ_CERT) { while(NULL!=(x=PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL))) { ret=X509_STORE_add_cert(store, x); if(ret==0) { X509_free(x); break; } } } else if(type==SSL_X509_OBJ_CRL) { while(NULL!=(x_crl=PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL))) { ret=X509_STORE_add_crl(store, x_crl); if(ret==0) { X509_CRL_free(x_crl); break; } } } if(ret==0) { BIO_free(bio); return -1; } BIO_free(bio); return 1; } UNUSED static int filter_pem_fn(const struct dirent * ent) { const char* fn_suffix=".pem"; if(strlen(ent->d_name)< strlen(fn_suffix)) { return 0; } if(0!=strcmp(ent->d_name+strlen(ent->d_name)-strlen(fn_suffix), fn_suffix)) { return 0; } return 1; } static X509_STORE* _X509_store_create(const char* pem_bundle, const char* pem_dir, struct cert_store_param* param) { int ret=0, n=0, i=0; X509_STORE* store=X509_STORE_new(); char path[TFE_STRING_MAX]={0}; if (store == NULL) { return NULL; } ret = X509_STORE_set_default_paths(store); if (ret == 0) { return NULL; } if(pem_bundle&&strlen(pem_bundle)>0) { ret = X509_STORE_load_locations(store, pem_bundle, NULL); if (ret == 0) { return NULL; } TFE_LOG_INFO(g_default_logger, "X509 trust store load pem boundle: %s", pem_bundle); } X509_VERIFY_PARAM *x509_param=NULL; if(param->check_crl) { x509_param = X509_VERIFY_PARAM_new(); X509_VERIFY_PARAM_set_flags(x509_param, X509_V_FLAG_CRL_CHECK); X509_STORE_set1_param(store, x509_param); X509_VERIFY_PARAM_free(x509_param); TFE_LOG_INFO(g_default_logger, "X509 trust store enable CRL check"); } struct dirent **namelist = NULL; if(pem_dir&&strlen(pem_dir)>0) { n=tfe_scandir(pem_dir, &namelist, NULL, (int (*)(const void*, const void*))alphasort); if(n < 0) { return store; } for(i=0;id_name); if(0==strcasecmp(namelist[i]->d_name+strlen(namelist[i]->d_name)-strlen(".pem"), ".pem")) { _X509_add_cert_or_crl_add(store, SSL_X509_OBJ_CERT, path); } else if(0==strcasecmp(namelist[i]->d_name+strlen(namelist[i]->d_name)-strlen(".crl"), ".crl")) { _X509_add_cert_or_crl_add(store, SSL_X509_OBJ_CRL, path); } TFE_LOG_INFO(g_default_logger, "X509 trust store found X509 additive trust CA: %s", path); free(namelist[i]); } free(namelist); } return store; } static MESA_htable_handle _create_mesa_htable(void) { UNUSED int ret=0; MESA_htable_handle htable = MESA_htable_born(); ret = __wrapper_MESA_htable_set_opt_int(htable, MHO_SCREEN_PRINT_CTRL, 0); ret = __wrapper_MESA_htable_set_opt_int(htable, MHO_THREAD_SAFE, 0); ret = __wrapper_MESA_htable_set_opt_int(htable, MHO_MUTEX_NUM, 16); ret = __wrapper_MESA_htable_set_opt_int(htable, MHO_HASH_SLOT_SIZE, 1024*4); ret = __wrapper_MESA_htable_set_opt_int(htable, MHO_HASH_MAX_ELEMENT_NUM, 0); ret = __wrapper_MESA_htable_set_opt_int(htable, MHO_EXPIRE_TIME, 0); ret = __wrapper_MESA_htable_set_opt_int(htable, MHO_ELIMIMINATE_TYPE, HASH_ELIMINATE_ALGO_FIFO); ret = __wrapper_MESA_htable_set_opt_func(htable, MHO_CBFUN_DATA_FREE, (void *)free_ssl_x509_obj, sizeof(&free_ssl_x509_obj)); //ret = __wrapper_MESA_htable_set_opt(htable, MHO_CBFUN_DATA_EXPIRE_NOTIFY, // (void *)key_keeper_verify_cb); ret = MESA_htable_mature(htable); return htable; } struct ssl_trusted_cert_storage* ssl_trusted_cert_storage_create(const char* pem_bundle, const char* pem_dir, struct cert_store_param* param) { struct ssl_trusted_cert_storage* storage=ALLOC(struct ssl_trusted_cert_storage, 1); storage->param=*param; storage->effective_store=_X509_store_create(pem_bundle, pem_dir, &(storage->param)); if (storage->effective_store == NULL) { return NULL; } storage->pem_bundle=tfe_strdup(pem_bundle); storage->pem_dir=tfe_strdup(pem_dir); storage->hash_table=_create_mesa_htable(); pthread_rwlock_init(&(storage->rwlock), NULL); assert(SSL_EX_DATA_IDX_VERIFY_PARAM<0); SSL_EX_DATA_IDX_VERIFY_PARAM = X509_STORE_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); return storage; } void ssl_trusted_cert_storage_destroy(struct ssl_trusted_cert_storage* storage) { return; } int ssl_trusted_cert_storage_add(struct ssl_trusted_cert_storage* storage, enum ssl_X509_obj_type type, const char* filename) { int ret=0; struct ssl_X509_object* obj=NULL; void* data=NULL; pthread_rwlock_wrlock(&(storage->rwlock)); data=MESA_htable_search(storage->hash_table, (const unsigned char*)filename, strlen(filename)); if(data!=NULL)//duplicated { ret=-1; goto error_out; } ret=_X509_add_cert_or_crl_add(storage->effective_store, type, filename); if(ret<0) { ret=-1; goto error_out; } obj=ALLOC(struct ssl_X509_object, 1); obj->type=SSL_X509_OBJ_CERT; obj->filename=tfe_strdup(filename); ret=MESA_htable_add(storage->hash_table, (const unsigned char*)obj->filename, strlen(obj->filename), obj); assert(ret>0); ret=1; error_out: pthread_rwlock_unlock(&(storage->rwlock)); return ret; } static void cert_storage_htable_traverse_cb(const uchar * key, uint size, void * data, void * user) { X509_STORE* store=(X509_STORE* )user; struct ssl_X509_object* obj=(struct ssl_X509_object*)data; _X509_add_cert_or_crl_add(store, obj->type, obj->filename); return; } int ssl_trusted_cert_storage_del(struct ssl_trusted_cert_storage* storage, enum ssl_X509_obj_type type, const char* filename) { int ret=0; X509_STORE* temp_store=NULL; pthread_rwlock_wrlock(&(storage->rwlock)); ret=MESA_htable_del(storage->hash_table, (const unsigned char*)filename, strlen(filename), NULL); if(ret<0) { ret=-1; goto error_out; } temp_store=_X509_store_create(storage->pem_bundle, storage->pem_dir, &(storage->param)); MESA_htable_iterate(storage->hash_table, cert_storage_htable_traverse_cb, temp_store); X509_STORE_free(storage->effective_store); storage->effective_store=temp_store; ret=1; error_out: pthread_rwlock_unlock(&(storage->rwlock)); return ret; } void ssl_trusted_cert_storage_reset(struct ssl_trusted_cert_storage* storage) { X509_STORE* temp_store=NULL; MESA_htable_destroy(storage->hash_table, NULL); storage->hash_table=_create_mesa_htable(); temp_store=_X509_store_create(storage->pem_bundle, storage->pem_dir, &(storage->param)); pthread_rwlock_wrlock(&(storage->rwlock)); X509_STORE_free(storage->effective_store); storage->effective_store=temp_store; pthread_rwlock_unlock(&(storage->rwlock)); return; } static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { int err=0, ret=0; struct cert_verify_param* param=NULL; if(preverify_ok) { return 1; } err = X509_STORE_CTX_get_error(ctx); /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ param = (struct cert_verify_param*)X509_STORE_CTX_get_ex_data(ctx, SSL_EX_DATA_IDX_VERIFY_PARAM); switch(err) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: if(param->no_verify_issuer) { ret=1; } break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: if(param->no_verify_self_signed) { ret=1; } break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_CERT_HAS_EXPIRED: if(param->no_verify_expiry_date) { ret=1; } break; case X509_V_ERR_UNABLE_TO_GET_CRL: case X509_V_ERR_DIFFERENT_CRL_SCOPE: case X509_V_ERR_CRL_HAS_EXPIRED: ret=1; break; default: ret=0; break; } return ret; } int ssl_trusted_cert_storage_verify_conn(struct ssl_trusted_cert_storage* storage, SSL * ssl, const char* hostname, struct cert_verify_param* param, char* reason, size_t n_reason, struct cert_verify_result* result) { int ret = 0, err_code=0; char *subj=NULL, *issuer=NULL; STACK_OF(X509) * cert_chain = SSL_get_peer_cert_chain(ssl); if (cert_chain == NULL) { // The peer certificate chain is not necessarily available after reusing a session, in which case a NULL pointer is returned. return 1; } X509 * cert = sk_X509_value(cert_chain, 0); if(!param->no_verify_cn&&hostname) { result->is_hostmatched=X509_check_host(cert, hostname, strlen(hostname), 0, NULL); } else { result->is_hostmatched=1; } char* oid=ssl_x509_get_extension(cert, NID_certificate_policies); if(oid) { result->is_ev=ssl_x509_is_ev(oid); } free(oid); oid=NULL; ASN1_OCTET_STRING *sct=NULL; int crit = 0; sct = (ASN1_OCTET_STRING*)X509_get_ext_d2i(cert, NID_ct_precert_scts, &crit, NULL); if(sct) { result->is_ct=1; } ASN1_STRING_free(sct); X509_STORE_CTX * ctx = X509_STORE_CTX_new(); pthread_rwlock_rdlock(&(storage->rwlock)); ret = X509_STORE_CTX_init(ctx, storage->effective_store, cert, cert_chain); assert(ret == 1); X509_STORE_CTX_set_verify_cb(ctx, verify_callback); X509_STORE_CTX_set_ex_data(ctx, SSL_EX_DATA_IDX_VERIFY_PARAM, param); //If a complete chain can be built and validated this function returns 1, otherwise it return zero or negtive code. ret = X509_verify_cert(ctx); err_code=X509_STORE_CTX_get_error(ctx); result->error_code=err_code; if(ret!=1) { subj=ssl_x509_subject(cert); issuer=ssl_x509_issuer(cert); snprintf(reason, n_reason, "%s : subject - %s issuer - %s", X509_verify_cert_error_string(err_code), subj, issuer); free(subj); free(issuer); ret=0; } else { ret=1; } X509_STORE_CTX_free(ctx); pthread_rwlock_unlock(&(storage->rwlock)); return ret; }