Skip to content

Commit

Permalink
feat(server): Add API functions for managing the server's trustlist
Browse files Browse the repository at this point in the history
  • Loading branch information
NoelGraf authored and jpfr committed Oct 16, 2024
1 parent fc74493 commit 7546d01
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 0 deletions.
40 changes: 40 additions & 0 deletions include/open62541/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -1909,6 +1909,46 @@ UA_Server_createSigningRequest(UA_Server *server,
const UA_ByteString *nonce,
UA_ByteString *csr);

/* Adds certificates and Certificate Revocation Lists (CRLs) to a specific certificate group
* on the server.
*
* @param server The server object
* @param certificateGroupId The NodeId of the certificate group where certificates will be added
* @param certificates The certificates to be added
* @param certificatesSize The number of certificates
* @param crls The associated CRLs for the certificates, required when adding issuer certificates
* @param crlsSize The number of CRLs
* @param isTrusted Indicates whether the certificates should be added to the trusted list or the issuer list
* @param appendCertificates Indicates whether the certificates should be added to the list or replace the existing list
* @return ``UA_STATUSCODE_GOOD`` on success
*/
UA_StatusCode UA_EXPORT
UA_Server_addCertificates(UA_Server *server,
const UA_NodeId certificateGroupId,
UA_ByteString *certificates,
size_t certificatesSize,
UA_ByteString *crls,
size_t crlsSize,
const UA_Boolean isTrusted,
const UA_Boolean appendCertificates);

/* Removes certificates from a specific certificate group on the server.
* The corresponding CRLs are removed automatically.
*
* @param server The server object
* @param certificateGroupId The NodeId of the certificate group from which certificates will be removed
* @param certificates The certificates to be removed
* @param certificatesSize The number of certificates
* @param isTrusted Indicates whether the certificates are being removed from the trusted list or the issuer list
* @return ``UA_STATUSCODE_GOOD`` on success
*/
UA_StatusCode UA_EXPORT
UA_Server_removeCertificates(UA_Server *server,
const UA_NodeId certificateGroupId,
UA_ByteString *certificates,
size_t certificatesSize,
const UA_Boolean isTrusted);

/**
* Utility Functions
* ----------------- */
Expand Down
141 changes: 141 additions & 0 deletions src/server/ua_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,147 @@ UA_Server_removeCallback(UA_Server *server, UA_UInt64 callbackId) {
UA_UNLOCK(&server->serviceMutex);
}

static void
secureChannel_delayedCloseTrustList(void *application, void *context) {
UA_DelayedCallback *dc = (UA_DelayedCallback*)context;
UA_Server *server = (UA_Server*)application;

UA_CertificateGroup certGroup = server->config.secureChannelPKI;
UA_SecureChannel *channel;
TAILQ_FOREACH(channel, &server->channels, serverEntry) {
const UA_SecurityPolicy *policy = channel->securityPolicy;
if(channel->state != UA_SECURECHANNELSTATE_CLOSED && channel->state != UA_SECURECHANNELSTATE_CLOSING)
continue;
if(certGroup.verifyCertificate(&certGroup, &policy->localCertificate) != UA_STATUSCODE_GOOD)
UA_SecureChannel_shutdown(channel, UA_SHUTDOWNREASON_CLOSE);
}
UA_free(dc);
}

static UA_CertificateGroup*
getCertificateGroup(UA_Server *server, const UA_NodeId certificateGroupId) {
UA_NodeId defaultApplicationGroup =
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
UA_NodeId defaultUserTokenGroup =
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
if(UA_NodeId_equal(&certificateGroupId, &defaultApplicationGroup)) {
return &server->config.secureChannelPKI;
}
if(UA_NodeId_equal(&certificateGroupId, &defaultUserTokenGroup)) {
return &server->config.sessionPKI;
}
return NULL;
}

UA_StatusCode
UA_Server_addCertificates(UA_Server *server,
const UA_NodeId certificateGroupId,
UA_ByteString *certificates,
size_t certificatesSize,
UA_ByteString *crls,
size_t crlsSize,
const UA_Boolean isTrusted,
const UA_Boolean appendCertificates) {
UA_CertificateGroup *certGroup = getCertificateGroup(server, certificateGroupId);
if(!certGroup)
return UA_STATUSCODE_BADINVALIDARGUMENT;

UA_TrustListDataType trustList;
UA_TrustListDataType_init(&trustList);

if(isTrusted) {
trustList.specifiedLists = UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES | UA_TRUSTLISTMASKS_TRUSTEDCRLS;
trustList.trustedCertificates = certificates;
trustList.trustedCertificatesSize = certificatesSize;
trustList.trustedCrls = crls;
trustList.trustedCrlsSize = crlsSize;
} else {
trustList.specifiedLists = UA_TRUSTLISTMASKS_ISSUERCERTIFICATES | UA_TRUSTLISTMASKS_ISSUERCRLS;
trustList.issuerCertificates = certificates;
trustList.issuerCertificatesSize = certificatesSize;
trustList.issuerCrls = crls;
trustList.issuerCrlsSize = crlsSize;
}

/* When adding certificate files to the TrustList,
* it is not necessary to check the trust status of the existing SecureChannels. */
if(appendCertificates)
return certGroup->addToTrustList(certGroup, &trustList);

UA_StatusCode retval = certGroup->setTrustList(certGroup, &trustList);
if(retval != UA_STATUSCODE_GOOD)
return retval;

UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_calloc(1, sizeof(UA_DelayedCallback));
if(!dc)
return UA_STATUSCODE_BADOUTOFMEMORY;

dc->callback = secureChannel_delayedCloseTrustList;
dc->application = server;
dc->context = dc;

UA_EventLoop *el = server->config.eventLoop;
el->addDelayedCallback(el, dc);

return UA_STATUSCODE_GOOD;
}

UA_StatusCode
UA_Server_removeCertificates(UA_Server *server,
const UA_NodeId certificateGroupId,
UA_ByteString *certificates,
size_t certificatesSize,
const UA_Boolean isTrusted) {
UA_CertificateGroup *certGroup = getCertificateGroup(server, certificateGroupId);
if(!certGroup)
return UA_STATUSCODE_BADINVALIDARGUMENT;

UA_ByteString *crls = NULL;
size_t crlsSize = 0;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
for(size_t i = 0; i < certificatesSize; i++) {
retval = certGroup->getCertificateCrls(certGroup, &certificates[i], isTrusted, &crls, &crlsSize);
if(retval != UA_STATUSCODE_GOOD) {
UA_Array_delete(crls, crlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
return retval;
}
}

UA_TrustListDataType trustList;
UA_TrustListDataType_init(&trustList);
if(isTrusted) {
trustList.specifiedLists = UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES | UA_TRUSTLISTMASKS_TRUSTEDCRLS;
trustList.trustedCertificates = certificates;
trustList.trustedCertificatesSize = certificatesSize;
trustList.trustedCrls = crls;
trustList.trustedCrlsSize = crlsSize;
} else {
trustList.specifiedLists = UA_TRUSTLISTMASKS_ISSUERCERTIFICATES | UA_TRUSTLISTMASKS_ISSUERCRLS;
trustList.issuerCertificates = certificates;
trustList.issuerCertificatesSize = certificatesSize;
trustList.issuerCrls = crls;
trustList.issuerCrlsSize = crlsSize;
}

retval = certGroup->removeFromTrustList(certGroup, &trustList);
UA_Array_delete(crls, crlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
if(retval != UA_STATUSCODE_GOOD)
return retval;

UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_calloc(1, sizeof(UA_DelayedCallback));
if(!dc)
return UA_STATUSCODE_BADOUTOFMEMORY;

dc->callback = secureChannel_delayedCloseTrustList;
dc->application = server;
dc->context = dc;

UA_EventLoop *el = server->config.eventLoop;
el->addDelayedCallback(el, dc);

return UA_STATUSCODE_GOOD;
}

typedef struct UpdateCertInfo {
UA_Server *server;
const UA_NodeId *certificateTypeId;
Expand Down

0 comments on commit 7546d01

Please sign in to comment.