| /* This work is licensed under a Creative Commons CCZero 1.0 Universal License. |
| * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. |
| * |
| * Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB |
| * Copyright 2019 (c) Kalycito Infotech Private Limited |
| * Copyright 2019 (c) Julius Pfrommer, Fraunhofer IOSB |
| */ |
| |
| #include <open62541/server_config.h> |
| #include <open62541/plugin/pki_default.h> |
| #include <open62541/plugin/log_stdout.h> |
| |
| #ifdef UA_ENABLE_ENCRYPTION |
| #include <mbedtls/x509.h> |
| #include <mbedtls/x509_crt.h> |
| #include <mbedtls/error.h> |
| #endif |
| |
| #define REMOTECERTIFICATETRUSTED 1 |
| #define ISSUERKNOWN 2 |
| #define DUALPARENT 3 |
| #define PARENTFOUND 4 |
| |
| /************/ |
| /* AllowAll */ |
| /************/ |
| |
| static UA_StatusCode |
| verifyCertificateAllowAll(void *verificationContext, |
| const UA_ByteString *certificate) { |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| static UA_StatusCode |
| verifyApplicationURIAllowAll(void *verificationContext, |
| const UA_ByteString *certificate, |
| const UA_String *applicationURI) { |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| static void |
| deleteVerifyAllowAll(UA_CertificateVerification *cv) { |
| |
| } |
| |
| void UA_CertificateVerification_AcceptAll(UA_CertificateVerification *cv) { |
| cv->verifyCertificate = verifyCertificateAllowAll; |
| cv->verifyApplicationURI = verifyApplicationURIAllowAll; |
| cv->deleteMembers = deleteVerifyAllowAll; |
| } |
| |
| #ifdef UA_ENABLE_ENCRYPTION |
| |
| typedef struct { |
| /* If the folders are defined, we use them to reload the certificates during |
| * runtime */ |
| UA_String trustListFolder; |
| UA_String issuerListFolder; |
| UA_String revocationListFolder; |
| |
| mbedtls_x509_crt certificateTrustList; |
| mbedtls_x509_crt certificateIssuerList; |
| mbedtls_x509_crl certificateRevocationList; |
| } CertInfo; |
| |
| #ifdef __linux__ /* Linux only so far */ |
| |
| #include <dirent.h> |
| #include <limits.h> |
| |
| static UA_StatusCode |
| fileNamesFromFolder(const UA_String *folder, size_t *pathsSize, UA_String **paths) { |
| char buf[PATH_MAX + 1]; |
| if(folder->length > PATH_MAX) |
| return UA_STATUSCODE_BADINTERNALERROR; |
| |
| memcpy(buf, folder->data, folder->length); |
| buf[folder->length] = 0; |
| |
| DIR *dir = opendir(buf); |
| if(!dir) |
| return UA_STATUSCODE_BADINTERNALERROR; |
| |
| *paths = (UA_String*)UA_Array_new(256, &UA_TYPES[UA_TYPES_STRING]); |
| if(*paths == NULL) { |
| closedir(dir); |
| return UA_STATUSCODE_BADOUTOFMEMORY; |
| } |
| |
| struct dirent *ent; |
| char buf2[PATH_MAX + 1]; |
| realpath(buf, buf2); |
| size_t pathlen = strlen(buf2); |
| *pathsSize = 0; |
| while((ent = readdir (dir)) != NULL && *pathsSize < 256) { |
| if(ent->d_type != DT_REG) |
| continue; |
| buf2[pathlen] = '/'; |
| buf2[pathlen+1] = 0; |
| strcat(buf2, ent->d_name); |
| (*paths)[*pathsSize] = UA_STRING_ALLOC(buf2); |
| *pathsSize += 1; |
| } |
| closedir(dir); |
| |
| if(*pathsSize == 0) { |
| UA_free(*paths); |
| *paths = NULL; |
| } |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| static UA_StatusCode |
| reloadCertificates(CertInfo *ci) { |
| UA_StatusCode retval = UA_STATUSCODE_GOOD; |
| int err = 0; |
| |
| /* Load the trustlists */ |
| if(ci->trustListFolder.length > 0) { |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the trust-list"); |
| mbedtls_x509_crt_free(&ci->certificateTrustList); |
| mbedtls_x509_crt_init(&ci->certificateTrustList); |
| |
| char f[PATH_MAX]; |
| memcpy(f, ci->trustListFolder.data, ci->trustListFolder.length); |
| f[ci->trustListFolder.length] = 0; |
| err = mbedtls_x509_crt_parse_path(&ci->certificateTrustList, f); |
| if(err == 0) { |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, |
| "Loaded certificate from %s", f); |
| } else { |
| char errBuff[300]; |
| mbedtls_strerror(err, errBuff, 300); |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, |
| "Failed to load certificate from %s", f); |
| } |
| } |
| |
| /* Load the revocationlists */ |
| if(ci->revocationListFolder.length > 0) { |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the revocation-list"); |
| size_t pathsSize = 0; |
| UA_String *paths = NULL; |
| retval = fileNamesFromFolder(&ci->revocationListFolder, &pathsSize, &paths); |
| if(retval != UA_STATUSCODE_GOOD) |
| return retval; |
| mbedtls_x509_crl_free(&ci->certificateRevocationList); |
| mbedtls_x509_crl_init(&ci->certificateRevocationList); |
| for(size_t i = 0; i < pathsSize; i++) { |
| char f[PATH_MAX]; |
| memcpy(f, paths[i].data, paths[i].length); |
| f[paths[i].length] = 0; |
| err = mbedtls_x509_crl_parse_file(&ci->certificateRevocationList, f); |
| if(err == 0) { |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, |
| "Loaded certificate from %.*s", |
| (int)paths[i].length, paths[i].data); |
| } else { |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, |
| "Failed to load certificate from %.*s", |
| (int)paths[i].length, paths[i].data); |
| } |
| } |
| UA_Array_delete(paths, pathsSize, &UA_TYPES[UA_TYPES_STRING]); |
| paths = NULL; |
| pathsSize = 0; |
| } |
| |
| /* Load the issuerlists */ |
| if(ci->issuerListFolder.length > 0) { |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the issuer-list"); |
| mbedtls_x509_crt_free(&ci->certificateIssuerList); |
| mbedtls_x509_crt_init(&ci->certificateIssuerList); |
| char f[PATH_MAX]; |
| memcpy(f, ci->issuerListFolder.data, ci->issuerListFolder.length); |
| f[ci->issuerListFolder.length] = 0; |
| err = mbedtls_x509_crt_parse_path(&ci->certificateIssuerList, f); |
| if(err == 0) { |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, |
| "Loaded certificate from %s", f); |
| } else { |
| UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, |
| "Failed to load certificate from %s", f); |
| } |
| } |
| |
| return retval; |
| } |
| |
| #endif |
| |
| static UA_StatusCode |
| certificateVerification_verify(void *verificationContext, |
| const UA_ByteString *certificate) { |
| CertInfo *ci = (CertInfo*)verificationContext; |
| if(!ci) |
| return UA_STATUSCODE_BADINTERNALERROR; |
| |
| #ifdef __linux__ /* Reload certificates if folder paths are specified */ |
| reloadCertificates(ci); |
| #endif |
| |
| /* if(ci->certificateTrustList.raw.len == 0) { */ |
| /* UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, */ |
| /* "No Trustlist loaded. Accepting the certificate."); */ |
| /* return UA_STATUSCODE_GOOD; */ |
| /* } */ |
| |
| /* Parse the certificate */ |
| mbedtls_x509_crt remoteCertificate; |
| |
| /* Temporary Object to parse the trustList */ |
| mbedtls_x509_crt *tempCert; |
| |
| /* Temporary Object to parse the revocationList */ |
| mbedtls_x509_crl *tempCrl; |
| |
| /* Temporary Object to identify the parent CA when there is no intermediate CA */ |
| mbedtls_x509_crt *parentCert; |
| |
| /* Temporary Object to identify the parent CA when there is intermediate CA */ |
| mbedtls_x509_crt *parentCert_2; |
| |
| /* Flag value to identify if the issuer certificate is found */ |
| int issuerKnown = 0; |
| |
| /* Flag value to identify if the parent certificate found */ |
| int parentFound = 0; |
| |
| mbedtls_x509_crt_init(&remoteCertificate); |
| int mbedErr = mbedtls_x509_crt_parse(&remoteCertificate, certificate->data, |
| certificate->length); |
| if(mbedErr) { |
| /* char errBuff[300]; */ |
| /* mbedtls_strerror(mbedErr, errBuff, 300); */ |
| /* UA_LOG_WARNING(data->policyContext->securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, */ |
| /* "Could not parse the remote certificate with error: %s", errBuff); */ |
| return UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
| } |
| |
| /* Verify */ |
| mbedtls_x509_crt_profile crtProfile = { |
| MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) | MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256), |
| 0xFFFFFF, 0x000000, 128 * 8 // in bits |
| }; // TODO: remove magic numbers |
| |
| uint32_t flags = 0; |
| mbedErr = mbedtls_x509_crt_verify_with_profile(&remoteCertificate, |
| &ci->certificateTrustList, |
| &ci->certificateRevocationList, |
| &crtProfile, NULL, &flags, NULL, NULL); |
| |
| /* Flag to check if the remote certificate is trusted or not */ |
| int TRUSTED = 0; |
| |
| /* Check if the remoteCertificate is present in the trustList while mbedErr value is not zero */ |
| if(mbedErr && !(flags & MBEDTLS_X509_BADCERT_EXPIRED) && !(flags & MBEDTLS_X509_BADCERT_FUTURE)) { |
| for(tempCert = &ci->certificateTrustList; tempCert != NULL; tempCert = tempCert->next) { |
| if(remoteCertificate.raw.len == tempCert->raw.len && |
| memcmp(remoteCertificate.raw.p, tempCert->raw.p, remoteCertificate.raw.len) == 0) { |
| TRUSTED = REMOTECERTIFICATETRUSTED; |
| break; |
| } |
| } |
| } |
| |
| /* If the remote certificate is present in the trustList then check if the issuer certificate |
| * of remoteCertificate is present in issuerList */ |
| if(TRUSTED && mbedErr) { |
| mbedErr = mbedtls_x509_crt_verify_with_profile(&remoteCertificate, |
| &ci->certificateIssuerList, |
| &ci->certificateRevocationList, |
| &crtProfile, NULL, &flags, NULL, NULL); |
| |
| /* Check if the parent certificate has a CRL file available */ |
| if(!mbedErr) { |
| /* Flag value to identify if that there is an intermediate CA present */ |
| int dualParent = 0; |
| |
| /* Identify the topmost parent certificate for the remoteCertificate */ |
| for( parentCert = &ci->certificateIssuerList; parentCert != NULL; parentCert = parentCert->next ) { |
| if(memcmp(remoteCertificate.issuer_raw.p, parentCert->subject_raw.p, parentCert->subject_raw.len) == 0) { |
| for(parentCert_2 = &ci->certificateTrustList; parentCert_2 != NULL; parentCert_2 = parentCert_2->next) { |
| if(memcmp(parentCert->issuer_raw.p, parentCert_2->subject_raw.p, parentCert_2->subject_raw.len) == 0) { |
| dualParent = DUALPARENT; |
| parentFound = PARENTFOUND; |
| break; |
| } |
| |
| } |
| |
| parentFound = PARENTFOUND; |
| } |
| |
| if(parentFound == PARENTFOUND) { |
| break; |
| } |
| |
| } |
| |
| /* Check if there is an intermediate certificate between the topmost parent |
| * certificate and child certificate |
| * If yes the topmost parent certificate is to be checked whether it has a |
| * CRL file avaiable */ |
| if(dualParent == DUALPARENT && parentFound == PARENTFOUND) { |
| parentCert = parentCert_2; |
| } |
| |
| /* If a parent certificate is found traverse the revocationList and identify |
| * if there is any CRL file that corresponds to the parentCertificate */ |
| if(parentFound == PARENTFOUND) { |
| tempCrl = &ci->certificateRevocationList; |
| while(tempCrl != NULL) { |
| if(tempCrl->version != 0 && |
| tempCrl->issuer_raw.len == parentCert->subject_raw.len && |
| memcmp(tempCrl->issuer_raw.p, |
| parentCert->subject_raw.p, |
| tempCrl->issuer_raw.len) == 0) { |
| issuerKnown = ISSUERKNOWN; |
| break; |
| } |
| |
| tempCrl = tempCrl->next; |
| } |
| |
| /* If the CRL file corresponding to the parent certificate is not present |
| * then return UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN */ |
| if(!issuerKnown) { |
| return UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN; |
| } |
| |
| } |
| |
| } |
| |
| } |
| else if(!mbedErr && !TRUSTED) { |
| /* This else if section is to identify if the parent certificate which is present in trustList |
| * has CRL file corresponding to it */ |
| |
| /* Identify the parent certificate of the remoteCertificate */ |
| for(parentCert = &ci->certificateTrustList; parentCert != NULL; parentCert = parentCert->next) { |
| if(memcmp(remoteCertificate.issuer_raw.p, parentCert->subject_raw.p, parentCert->subject_raw.len) == 0) { |
| parentFound = PARENTFOUND; |
| break; |
| } |
| |
| } |
| |
| /* If the parent certificate is found traverse the revocationList and identify |
| * if there is any CRL file that corresponds to the parentCertificate */ |
| if(parentFound == PARENTFOUND && |
| memcmp(remoteCertificate.issuer_raw.p, remoteCertificate.subject_raw.p, remoteCertificate.subject_raw.len) != 0) { |
| tempCrl = &ci->certificateRevocationList; |
| while(tempCrl != NULL) { |
| if(tempCrl->version != 0 && |
| tempCrl->issuer_raw.len == parentCert->subject_raw.len && |
| memcmp(tempCrl->issuer_raw.p, |
| parentCert->subject_raw.p, |
| tempCrl->issuer_raw.len) == 0) { |
| issuerKnown = ISSUERKNOWN; |
| break; |
| } |
| |
| tempCrl = tempCrl->next; |
| } |
| |
| /* If the CRL file corresponding to the parent certificate is not present |
| * then return UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN */ |
| if(!issuerKnown) { |
| return UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN; |
| } |
| |
| } |
| |
| } |
| |
| // TODO: Extend verification |
| |
| /* This condition will check whether the certificate is a User certificate |
| * or a CA certificate. If the MBEDTLS_X509_KU_KEY_CERT_SIGN and |
| * MBEDTLS_X509_KU_CRL_SIGN of key_usage are set, then the certificate |
| * shall be condidered as CA Certificate and cannot be used to establish a |
| * connection. Refer the test case CTT/Security/Security Certificate Validation/029.js |
| * for more details */ |
| if((remoteCertificate.key_usage & MBEDTLS_X509_KU_KEY_CERT_SIGN) && |
| (remoteCertificate.key_usage & MBEDTLS_X509_KU_CRL_SIGN)) { |
| return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; |
| } |
| |
| UA_StatusCode retval = UA_STATUSCODE_GOOD; |
| if(mbedErr) { |
| /* char buff[100]; */ |
| /* mbedtls_x509_crt_verify_info(buff, 100, "", flags); */ |
| /* UA_LOG_ERROR(channelContextData->policyContext->securityPolicy->logger, */ |
| /* UA_LOGCATEGORY_SECURITYPOLICY, */ |
| /* "Verifying the certificate failed with error: %s", buff); */ |
| |
| if(flags & (uint32_t)MBEDTLS_X509_BADCERT_NOT_TRUSTED) { |
| retval = UA_STATUSCODE_BADCERTIFICATEUNTRUSTED; |
| } else if(flags & (uint32_t)MBEDTLS_X509_BADCERT_FUTURE || |
| flags & (uint32_t)MBEDTLS_X509_BADCERT_EXPIRED) { |
| retval = UA_STATUSCODE_BADCERTIFICATETIMEINVALID; |
| } else if(flags & (uint32_t)MBEDTLS_X509_BADCERT_REVOKED || |
| flags & (uint32_t)MBEDTLS_X509_BADCRL_EXPIRED) { |
| retval = UA_STATUSCODE_BADCERTIFICATEREVOKED; |
| } else { |
| retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
| } |
| } |
| |
| mbedtls_x509_crt_free(&remoteCertificate); |
| return retval; |
| } |
| |
| /* Find binary substring. Taken and adjusted from |
| * http://tungchingkai.blogspot.com/2011/07/binary-strstr.html */ |
| |
| static const unsigned char * |
| bstrchr(const unsigned char *s, const unsigned char ch, size_t l) { |
| /* find first occurrence of c in char s[] for length l*/ |
| /* handle special case */ |
| if(l == 0) |
| return (NULL); |
| |
| for(; *s != ch; ++s, --l) |
| if(l == 0) |
| return (NULL); |
| return s; |
| } |
| |
| static const unsigned char * |
| bstrstr(const unsigned char *s1, size_t l1, const unsigned char *s2, size_t l2) { |
| /* find first occurrence of s2[] in s1[] for length l1*/ |
| const unsigned char *ss1 = s1; |
| const unsigned char *ss2 = s2; |
| /* handle special case */ |
| if(l1 == 0) |
| return (NULL); |
| if(l2 == 0) |
| return s1; |
| |
| /* match prefix */ |
| for (; (s1 = bstrchr(s1, *s2, (uintptr_t)ss1-(uintptr_t)s1+(uintptr_t)l1)) != NULL && |
| (uintptr_t)ss1-(uintptr_t)s1+(uintptr_t)l1 != 0; ++s1) { |
| |
| /* match rest of prefix */ |
| const unsigned char *sc1, *sc2; |
| for (sc1 = s1, sc2 = s2; ;) |
| if (++sc2 >= ss2+l2) |
| return s1; |
| else if (*++sc1 != *sc2) |
| break; |
| } |
| return NULL; |
| } |
| |
| static UA_StatusCode |
| certificateVerification_verifyApplicationURI(void *verificationContext, |
| const UA_ByteString *certificate, |
| const UA_String *applicationURI) { |
| CertInfo *ci = (CertInfo*)verificationContext; |
| if(!ci) |
| return UA_STATUSCODE_BADINTERNALERROR; |
| |
| /* Parse the certificate */ |
| mbedtls_x509_crt remoteCertificate; |
| mbedtls_x509_crt_init(&remoteCertificate); |
| int mbedErr = mbedtls_x509_crt_parse(&remoteCertificate, certificate->data, |
| certificate->length); |
| if(mbedErr) |
| return UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
| |
| /* Poor man's ApplicationUri verification. mbedTLS does not parse all fields |
| * of the Alternative Subject Name. Instead test whether the URI-string is |
| * present in the v3_ext field in general. |
| * |
| * TODO: Improve parsing of the Alternative Subject Name */ |
| UA_StatusCode retval = UA_STATUSCODE_GOOD; |
| if(bstrstr(remoteCertificate.v3_ext.p, remoteCertificate.v3_ext.len, |
| applicationURI->data, applicationURI->length) == NULL) |
| retval = UA_STATUSCODE_BADCERTIFICATEURIINVALID; |
| |
| mbedtls_x509_crt_free(&remoteCertificate); |
| return retval; |
| } |
| |
| static void |
| certificateVerification_deleteMembers(UA_CertificateVerification *cv) { |
| CertInfo *ci = (CertInfo*)cv->context; |
| if(!ci) |
| return; |
| mbedtls_x509_crt_free(&ci->certificateTrustList); |
| mbedtls_x509_crl_free(&ci->certificateRevocationList); |
| mbedtls_x509_crt_free(&ci->certificateIssuerList); |
| UA_String_clear(&ci->trustListFolder); |
| UA_String_clear(&ci->issuerListFolder); |
| UA_String_clear(&ci->revocationListFolder); |
| UA_free(ci); |
| cv->context = NULL; |
| } |
| |
| UA_StatusCode |
| UA_CertificateVerification_Trustlist(UA_CertificateVerification *cv, |
| const UA_ByteString *certificateTrustList, |
| size_t certificateTrustListSize, |
| const UA_ByteString *certificateIssuerList, |
| size_t certificateIssuerListSize, |
| const UA_ByteString *certificateRevocationList, |
| size_t certificateRevocationListSize) { |
| CertInfo *ci = (CertInfo*)UA_malloc(sizeof(CertInfo)); |
| if(!ci) |
| return UA_STATUSCODE_BADOUTOFMEMORY; |
| memset(ci, 0, sizeof(CertInfo)); |
| mbedtls_x509_crt_init(&ci->certificateTrustList); |
| mbedtls_x509_crl_init(&ci->certificateRevocationList); |
| mbedtls_x509_crt_init(&ci->certificateIssuerList); |
| |
| cv->context = (void*)ci; |
| if(certificateTrustListSize > 0) |
| cv->verifyCertificate = certificateVerification_verify; |
| else |
| cv->verifyCertificate = verifyCertificateAllowAll; |
| cv->deleteMembers = certificateVerification_deleteMembers; |
| cv->verifyApplicationURI = certificateVerification_verifyApplicationURI; |
| |
| int err = 0; |
| for(size_t i = 0; i < certificateTrustListSize; i++) { |
| err = mbedtls_x509_crt_parse(&ci->certificateTrustList, |
| certificateTrustList[i].data, |
| certificateTrustList[i].length); |
| if(err) |
| goto error; |
| } |
| for(size_t i = 0; i < certificateIssuerListSize; i++) { |
| err = mbedtls_x509_crt_parse(&ci->certificateIssuerList, |
| certificateIssuerList[i].data, |
| certificateIssuerList[i].length); |
| if(err) |
| goto error; |
| } |
| for(size_t i = 0; i < certificateRevocationListSize; i++) { |
| err = mbedtls_x509_crl_parse(&ci->certificateRevocationList, |
| certificateRevocationList[i].data, |
| certificateRevocationList[i].length); |
| if(err) |
| goto error; |
| } |
| |
| return UA_STATUSCODE_GOOD; |
| error: |
| certificateVerification_deleteMembers(cv); |
| return UA_STATUSCODE_BADINTERNALERROR; |
| } |
| |
| #ifdef __linux__ /* Linux only so far */ |
| |
| UA_StatusCode |
| UA_CertificateVerification_CertFolders(UA_CertificateVerification *cv, |
| const char *trustListFolder, |
| const char *issuerListFolder, |
| const char *revocationListFolder) { |
| CertInfo *ci = (CertInfo*)UA_malloc(sizeof(CertInfo)); |
| if(!ci) |
| return UA_STATUSCODE_BADOUTOFMEMORY; |
| memset(ci, 0, sizeof(CertInfo)); |
| mbedtls_x509_crt_init(&ci->certificateTrustList); |
| mbedtls_x509_crl_init(&ci->certificateRevocationList); |
| mbedtls_x509_crt_init(&ci->certificateIssuerList); |
| |
| /* Only set the folder paths. They will be reloaded during runtime. |
| * TODO: Add a more efficient reloading of only the changes */ |
| ci->trustListFolder = UA_STRING_ALLOC(trustListFolder); |
| ci->issuerListFolder = UA_STRING_ALLOC(issuerListFolder); |
| ci->revocationListFolder = UA_STRING_ALLOC(revocationListFolder); |
| |
| reloadCertificates(ci); |
| |
| cv->context = (void*)ci; |
| cv->verifyCertificate = certificateVerification_verify; |
| cv->deleteMembers = certificateVerification_deleteMembers; |
| cv->verifyApplicationURI = certificateVerification_verifyApplicationURI; |
| |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| #endif |
| |
| #endif |