/** * OpenSSL certificate chain verification callback * Verifies certificate chain, setting up context for fetch_curl_verify_callback */ static int fetch_curl_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *parm) { int ok; /* Store fetch struct in context for verify callback */ ok = X509_STORE_CTX_set_app_data(x509_ctx, parm); /* and verify the certificate chain */ if (ok) ok = X509_verify_cert(x509_ctx); return ok; }
/*============================================================================ * OpcUa_P_OpenSSL_PKI_ValidateCertificate *===========================================================================*/ OpcUa_StatusCode OpcUa_P_OpenSSL_PKI_ValidateCertificate( OpcUa_PKIProvider* a_pProvider, OpcUa_ByteString* a_pCertificate, OpcUa_Void* a_pCertificateStore, OpcUa_Int* a_pValidationCode /* Validation return codes from OpenSSL */ ) { OpcUa_P_OpenSSL_CertificateStore_Config* pCertificateStoreCfg; const unsigned char* p; X509* pX509Certificate = OpcUa_Null; STACK_OF(X509)* pX509Chain = OpcUa_Null; X509_STORE_CTX* verify_ctx = OpcUa_Null; /* holds data used during verification process */ char CertFile[MAX_PATH]; struct dirent **dirlist = NULL; int numCertificates = 0, i; OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "PKI_ValidateCertificate"); OpcUa_ReturnErrorIfArgumentNull(a_pProvider); OpcUa_ReturnErrorIfArgumentNull(a_pProvider->Handle); OpcUa_ReturnErrorIfArgumentNull(a_pCertificate); OpcUa_ReturnErrorIfArgumentNull(a_pCertificateStore); OpcUa_ReturnErrorIfArgumentNull(a_pValidationCode); pCertificateStoreCfg = (OpcUa_P_OpenSSL_CertificateStore_Config*)a_pProvider->Handle; /* convert DER encoded bytestring certificate to openssl X509 certificate */ p = a_pCertificate->Data; if(!(pX509Certificate = d2i_X509((X509**)OpcUa_Null, &p, a_pCertificate->Length))) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } while(p < a_pCertificate->Data + a_pCertificate->Length) { X509* pX509AddCertificate; if(!(pX509AddCertificate = d2i_X509((X509**)OpcUa_Null, &p, a_pCertificate->Data + a_pCertificate->Length - p))) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } if(pX509Chain == NULL) { pX509Chain = sk_X509_new_null(); OpcUa_GotoErrorIfAllocFailed(pX509Chain); } if(!sk_X509_push(pX509Chain, pX509AddCertificate)) { X509_free(pX509AddCertificate); OpcUa_GotoErrorWithStatus(OpcUa_Bad); } } /* create verification context and initialize it */ if(!(verify_ctx = X509_STORE_CTX_new())) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } #if (OPENSSL_VERSION_NUMBER > 0x00907000L) if(X509_STORE_CTX_init(verify_ctx, (X509_STORE*)a_pCertificateStore, pX509Certificate, pX509Chain) != 1) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } #else X509_STORE_CTX_init(verify_ctx, (X509_STORE*)a_pCertificateStore, pX509Certificate, pX509Chain); #endif if(X509_STORE_CTX_set_app_data(verify_ctx, pCertificateStoreCfg) != 1) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } if((pCertificateStoreCfg->Flags & OPCUA_P_PKI_OPENSSL_CHECK_REVOCATION_ALL) == OPCUA_P_PKI_OPENSSL_CHECK_REVOCATION_ALL_EXCEPT_SELF_SIGNED && !verify_ctx->check_issued(verify_ctx, pX509Certificate, pX509Certificate)) { /* set the flags of the store so that CRLs are consulted */ X509_STORE_CTX_set_flags(verify_ctx, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } /* verify the certificate */ *a_pValidationCode = X509_V_OK; if(X509_verify_cert(verify_ctx) <= 0) { *a_pValidationCode = verify_ctx->error; switch(verify_ctx->error) { case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_CRL_NOT_YET_VALID: case X509_V_ERR_CRL_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: { uStatus = OpcUa_BadCertificateTimeInvalid; break; } case X509_V_ERR_CERT_REVOKED: { uStatus = OpcUa_BadCertificateRevoked; break; } case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: { uStatus = OpcUa_BadCertificateUntrusted; break; } case X509_V_ERR_CERT_SIGNATURE_FAILURE: { uStatus = OpcUa_BadSecurityChecksFailed; break; } default: { uStatus = OpcUa_BadCertificateInvalid; } } OpcUa_GotoErrorIfBad(uStatus); } if(pCertificateStoreCfg->Flags & OPCUA_P_PKI_OPENSSL_REQUIRE_CHAIN_CERTIFICATE_IN_TRUST_LIST) { FILE* pCertificateFile; X509* pTrustCert; STACK_OF(X509)* chain; int trusted, n; chain = X509_STORE_CTX_get_chain(verify_ctx); trusted = 0; if(pCertificateStoreCfg->CertificateTrustListLocation == NULL || pCertificateStoreCfg->CertificateTrustListLocation[0] == '\0') { uStatus = OpcUa_Bad; OpcUa_GotoErrorIfBad(uStatus); } numCertificates = scandir(pCertificateStoreCfg->CertificateTrustListLocation, &dirlist, certificate_filter_der, alphasort); for (i=0; i<numCertificates; i++) { uStatus = OpcUa_P_OpenSSL_BuildFullPath(pCertificateStoreCfg->CertificateTrustListLocation, dirlist[i]->d_name, MAX_PATH, CertFile); OpcUa_GotoErrorIfBad(uStatus); /* read DER certificates */ pCertificateFile = fopen(CertFile, "r"); if(pCertificateFile == OpcUa_Null) { continue; /* ignore access errors */ } pTrustCert = d2i_X509_fp(pCertificateFile, (X509**)OpcUa_Null); fclose(pCertificateFile); if(pTrustCert == OpcUa_Null) { continue; /* ignore parse errors */ } for(n = 0; n < sk_X509_num(chain); n++) { if (X509_cmp(sk_X509_value(chain, n), pTrustCert) == 0) break; } X509_free(pTrustCert); if(n < sk_X509_num(chain)) { trusted = 1; break; } } for (i=0; i<numCertificates; i++) { free(dirlist[i]); } free(dirlist); dirlist = NULL; if(!trusted) { uStatus = OpcUa_BadCertificateUntrusted; OpcUa_GotoErrorIfBad(uStatus); } } X509_STORE_CTX_free(verify_ctx); X509_free(pX509Certificate); if(pX509Chain != OpcUa_Null) { sk_X509_pop_free(pX509Chain, X509_free); } OpcUa_ReturnStatusCode; OpcUa_BeginErrorHandling; if(dirlist != NULL) { for (i=0; i<numCertificates; i++) { free(dirlist[i]); } free(dirlist); } if(verify_ctx != OpcUa_Null) { X509_STORE_CTX_free(verify_ctx); } if(pX509Certificate != OpcUa_Null) { X509_free(pX509Certificate); } if(pX509Chain != OpcUa_Null) { sk_X509_pop_free(pX509Chain, X509_free); } OpcUa_FinishErrorHandling; }