Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: simple-mbed-cloud-client/mbed-cloud-client/certificate-enrollment-client/source/CertificateEnrollmentClient.cpp
- Revision:
- 0:8f0bb79ddd48
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simple-mbed-cloud-client/mbed-cloud-client/certificate-enrollment-client/source/CertificateEnrollmentClient.cpp Tue May 04 08:55:12 2021 +0000 @@ -0,0 +1,587 @@ +// ---------------------------------------------------------------------------- +// Copyright 2018 ARM Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + + +#include "CertificateEnrollmentClient.h" +#include "mbed-client/m2mresource.h" +#include "mbed-client/m2minterfacefactory.h" +#include "mbed-client/m2minterface.h" +#include "pal.h" +#include "eventOS_scheduler.h" +#include "eventOS_event.h" +#include "ce_defs.h" +#include "ce_tlv.h" +#include "certificate_enrollment.h" +#include <stdio.h> +#include <string.h> +#include "CertificateEnrollmentClient.h" +#include "CertificateEnrollmentClientCommon.h" +#include "CertificateRenewalData.h" +#include "pv_error_handling.h" +#include "pv_macros.h" + +#define RESOURCE_ID_CERTIFICATE_NAME "27002" +#define OBJECT_LWM2M_CERTIFICATE "35011" + +#define NUMBER_OF_CONCURRENT_RENEWALS 1 + + +/************************************************************************/ +/* Different calls to update */ +/************************************************************************/ + +extern const char g_lwm2m_name[]; + +namespace CertificateEnrollmentClient { + + // Event type that is part of the arm_event_s structure. + enum event_type_e { + EVENT_TYPE_INIT, // Some initializer - nothing done currently, initialization called by mbed cloud client + EVENT_TYPE_RENEWAL_REQUEST, // Certificate renewal request. We can tell if it is initiated by the server or the device with the derived type of CertificateRenewalDataBase of the certificate descriptor / global variable + EVENT_TYPE_EST_RESPONDED, // Certificate arrived from EST service, or EST failure + EVENT_TYPE_MAX = 0xff // Must fit in a uint8_t (field in the arm_event_s struct) + }; + + // Pointer to the EST client object for dealing with the EST service. + extern const CERT_ENROLLMENT_EST_CLIENT *g_est_client; + + // Data for certificate that is currently being renewed. Will change to list if we need to support multiple renewals + static CertificateRenewalDataBase *current_cert = NULL; + + // ID of the handler we register to the MbedCloudClient event loop + static int8_t handler_id = -1; + + // Flag that indicates whether the module is initialized + static bool is_initialized = false; + + // Semaphore for enforcing that only NUMBER_OF_CONCURRENT_RENEWALS (currently 1) request at a time may update current_cert. Hold lock until process finished. + // Important: When pal_osSemaphoreWait called from within event loop - do not block, must set timeout to 0, and fail if failed to acquire lock + // Future: For supporting renewals of NUMBER_OF_CONCURRENT_RENEWALS certificates simultaneously - change to semaphore that counts to NUMBER_OF_CONCURRENT_RENEWALS + // and maintain a list of certificates of size NUMBER_OF_CONCURRENT_RENEWALS inside the event loop. + static palSemaphoreID_t g_renewal_sem = 0; + + + /** + * \brief Finish the renewal process. + * Zero current_cert pointer, then release the semaphore. Note that when the semaphore is released - new device renewals may be made. + * Then call renewal_data->finish() and delete renewal_data. + * + * \param renewal_data the data of the certificate to be renewed. + * It is important that this is passed to the function because after releasing the semaphore - the global pointer may be replaced. + * \param exit_status the status of the renewal process + */ + static void certificate_renewal_finish(CertificateRenewalDataBase *renewal_data, ce_status_e exit_status); + + /** + * \brief The function that handles all the CertificateEnrollmentClient events + * Create an arm_event_s object and call eventOS_event_send() + * The event will have an application level priority, and will be executed when in the head of the event queue. + * In the future: an extra arg should be passed - some descriptor for the specific CertificateRenewalDataBase object. + * + * \param event_type An event identifier + */ + static void event_handler(arm_event_s* event); + + /** + * \brief Send a new event to the event loop queue. + * Create an arm_event_s object and call eventOS_event_send() + * The event will have an application level priority + * + * \param renewal_data A pointer to an object derived from CertificateRenewalDataBase + * \param event_type An event identifier + */ + static ce_status_e schedule_event(event_type_e event_type); + + /** + * \brief Callback that will be executed when an EST service response is available + * Event Context is probably network so this function will check if response is success, if so, allocate the data if needed in renewal_data->est_data. schedule a new event with type EVENT_TYPE_EST_RESPONDED. + * + * \param result Whether the EST client successfully received a certificate from the EST service + * \param cert_chain structure containing the certificate/chain received from the EST service + * \param Context passed when requesting a certificate via the EST client. Currently unused. In the future will be used to identify the relevant CertificateRenewalDataBase object. + */ + static void est_cb(est_enrollment_result_e result, + cert_chain_context_s *cert_chain, + void *context); + + /** + * \brief The function that handles the EST response. + * + * Called by event_handler(). renewal_data->est_data already exists and is valid. + * Will perform a safe replacement of the certificate with the new certificate received from EST + * Then it will free the EST chain and finish the renewal operation. + * \param renewal_data A pointer to an object derived from CertificateRenewalDataBase + */ + static void est_response_process(CertificateRenewalDataBase *renewal_data); + + /** + * \brief Create g_cert_enroll_lwm2m_obj, from the object create an object resource, and create the resources. Then push the object to the MCC object list + * + * Note that the pointers to the objects created by this function is owned by the CertificateEnrollmentClient Module and must be released in by CertificateEnrollmentClient::finalize() + * \param list A reference to the MbedCloudClient object list. MbedCloudClient will later set the resource + */ + static ce_status_e init_objects(M2MBaseList& list); + + /** + * \brief Release the objects created by init_objects() + * + */ + static void release_objects(); + + // Callback is called when we get a POST message to g_cert_enroll_lwm2m_resource (runs in high priority context!) + /** + * \brief Callback is called when we get a POST message to g_cert_enroll_lwm2m_resource + * Runs in network context of the event loop. + * This function extracts the input data, creates a CertificateEnrollmentClient::CertificateRenewalDataFromServer object sets the global variable + * + * \param arg a M2MResource::M2MExecuteParameter argument. + */ + static void certificate_renewal_post(void *arg); + + /** + * \brief Start the renewal process. + * Parse the certificate name, generate keys and CSR. Then call the EST client so the new certificate may be retrieved + * + * \param renewal_data the data of the certificate to be renewed + */ + static void certificate_renewal_start(CertificateRenewalDataBase *renewal_data); + + /** + * \brief Call the user callback and send a response to the server, when a CertificateRenewalDataFromServer object does not exist. + * Use only for server initiated renewal, since this sends a response to the server. + * + * \param tlv the raw data from the server - should be in the form of TLV + * \param tlv_size size of the TLV buffer + * \param ret_status The return status to return to the user callback and the server + */ + static void call_user_cb_send_response(const uint8_t *tlv, uint16_t tlv_size, ce_status_e ret_status); + +#ifdef CERT_RENEWAL_TEST + void testonly_certificate_renewal_post(void *arg); +#endif // CERT_RENEWAL_TEST + +} + +// FIXME: print error +void CertificateEnrollmentClient::call_user_cb_send_response(const uint8_t *tlv, uint16_t tlv_size, ce_status_e ret_status) +{ + CertificateEnrollmentClient::CertificateRenewalDataFromServer temp_obj(tlv, tlv_size); + (void)temp_obj.parse(); + + // Call user callback with appropriate error + // In case of parsing error (malformed TLV), the provided ret_status will be returned and not + call_user_cert_renewal_cb(temp_obj.cert_name, ret_status, CE_INITIATOR_SERVER); + + // Send response to the server + SA_PV_LOG_INFO("sending delayed response\n"); + g_cert_enroll_lwm2m_resource->set_value(ret_status); + g_cert_enroll_lwm2m_resource->send_delayed_post_response(); +} + +void CertificateEnrollmentClient::certificate_renewal_post(void *arg) +{ + palStatus_t pal_status; + ce_status_e status; + SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS(); + + M2MResource::M2MExecuteParameter *args = (M2MResource::M2MExecuteParameter *)arg; + const uint8_t *data = args->get_argument_value(); + const uint16_t data_size = args->get_argument_value_length(); + + // If CEC module is not initialized - do not even take semaphore - exit with proper response + SA_PV_ERR_RECOVERABLE_RETURN_IF((!is_initialized), call_user_cb_send_response(data, data_size, CE_STATUS_NOT_INITIALIZED), "Certificate Renewal module not initialized"); + + pal_status = pal_osSemaphoreWait(g_renewal_sem, 0, NULL); + + if (pal_status == PAL_SUCCESS) { + CertificateEnrollmentClient::current_cert = new CertificateEnrollmentClient::CertificateRenewalDataFromServer(data, data_size); + if (!CertificateEnrollmentClient::current_cert) { + status = CE_STATUS_OUT_OF_MEMORY; + pal_status = pal_osSemaphoreRelease(g_renewal_sem); + if (PAL_SUCCESS != pal_status) { // Should never happen + status = CE_STATUS_ERROR; + } + + call_user_cb_send_response(data, data_size, status); + return; + } + + // Enqueue the event + status = schedule_event(CertificateEnrollmentClient::EVENT_TYPE_RENEWAL_REQUEST); + SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_STATUS_SUCCESS), certificate_renewal_finish(CertificateEnrollmentClient::current_cert, status), "Error scheduling event"); + + } else { + SA_PV_LOG_ERR("Failed to take semaphore- device busy\n"); + + if (pal_status == PAL_ERR_RTOS_TIMEOUT) { + status = CE_STATUS_DEVICE_BUSY; + } else { + status = CE_STATUS_ERROR; + } + + call_user_cb_send_response(data, data_size, status); + return; + } + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); +} + +ce_status_e CertificateEnrollmentClient::certificate_renew(const char *cert_name) +{ + palStatus_t pal_status = PAL_SUCCESS; + ce_status_e status = CE_STATUS_SUCCESS; + + SA_PV_ERR_RECOVERABLE_RETURN_IF((!cert_name), CE_STATUS_INVALID_PARAMETER, "Provided NULL certificate name"); + SA_PV_ERR_RECOVERABLE_RETURN_IF((!is_initialized), CE_STATUS_NOT_INITIALIZED, "Certificate Renewal module not initialized"); + + SA_PV_LOG_INFO_FUNC_ENTER("cert_name = %s\n", cert_name); + + pal_status = pal_osSemaphoreWait(g_renewal_sem, 0, NULL); + + if (pal_status == PAL_SUCCESS) { + CertificateEnrollmentClient::current_cert = new CertificateEnrollmentClient::CertificateRenewalDataFromDevice(cert_name); + SA_PV_ERR_RECOVERABLE_GOTO_IF((!CertificateEnrollmentClient::current_cert), status = CE_STATUS_OUT_OF_MEMORY, ReleseSemReturn, "Allocation error"); + + // Enqueue the event + status = schedule_event(CertificateEnrollmentClient::EVENT_TYPE_RENEWAL_REQUEST); + SA_PV_ERR_RECOVERABLE_GOTO_IF((status != CE_STATUS_SUCCESS), status = status, ReleseSemReturn, "Error scheduling event"); + + // If some error synchronous error has occurred before scheduling the event - release the semaphore we had just taken, + // and then return the error without calling the user callback +ReleseSemReturn: + if (status != CE_STATUS_SUCCESS) { + pal_status = pal_osSemaphoreRelease(g_renewal_sem); + if (PAL_SUCCESS != pal_status) { // Should never happen + status = CE_STATUS_ERROR; + } + } + + } else { + // return with appropriate error + if (pal_status == PAL_ERR_RTOS_TIMEOUT) { + status = CE_STATUS_DEVICE_BUSY; + } else { + status = CE_STATUS_ERROR; + } + + } + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); + return status; +} + + +void CertificateEnrollmentClient::on_certificate_renewal(cert_renewal_cb_f user_cb) +{ + SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS(); + CertificateEnrollmentClient::set_user_cert_renewal_cb(user_cb); + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); +} + + +ce_status_e CertificateEnrollmentClient::init_objects(M2MBaseList& list) +{ + M2MObjectInstance *cert_enroll_lwm2m_obj_instance; + + ce_status_e ce_status = CE_STATUS_SUCCESS; + SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS(); + + // Create the certificate enrollment resource + g_cert_enroll_lwm2m_obj = M2MInterfaceFactory::create_object(OBJECT_LWM2M_CERTIFICATE); + SA_PV_ERR_RECOVERABLE_RETURN_IF((!g_cert_enroll_lwm2m_obj), CE_STATUS_ERROR, "Error creating LWM2M object"); + + // Create the instance + cert_enroll_lwm2m_obj_instance = g_cert_enroll_lwm2m_obj->create_object_instance(); + SA_PV_ERR_RECOVERABLE_GOTO_IF((!cert_enroll_lwm2m_obj_instance), ce_status = CE_STATUS_ERROR, Cleanup, "Error creating LWM2M object instance"); + + // Create the resource + g_cert_enroll_lwm2m_resource = cert_enroll_lwm2m_obj_instance->create_dynamic_resource(RESOURCE_ID_CERTIFICATE_NAME, "Enroll", M2MResourceInstance::INTEGER, false); + SA_PV_ERR_RECOVERABLE_GOTO_IF((!g_cert_enroll_lwm2m_resource), ce_status = CE_STATUS_ERROR, Cleanup, "Error creating LWM2M resource"); + + // Allow POST operations + g_cert_enroll_lwm2m_resource->set_operation(M2MBase::POST_ALLOWED); + + // Set the resource callback + SA_PV_ERR_RECOVERABLE_GOTO_IF((!g_cert_enroll_lwm2m_resource->set_execute_function(CertificateEnrollmentClient::certificate_renewal_post)), + ce_status = CE_STATUS_ERROR, Cleanup, "Error resource callback"); + + // Enable sending of delayed responses + g_cert_enroll_lwm2m_resource->set_delayed_response(true); + + // Push the object to the list + list.push_back(g_cert_enroll_lwm2m_obj); + +Cleanup: + if (ce_status != CE_STATUS_SUCCESS) { + // Destroying the object will destroy all instances and resources associated with it + delete g_cert_enroll_lwm2m_obj; + g_cert_enroll_lwm2m_resource = NULL; + } + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); + return ce_status; +} + +void CertificateEnrollmentClient::release_objects() +{ + delete g_cert_enroll_lwm2m_obj; + g_cert_enroll_lwm2m_obj = NULL; +} + + +ce_status_e CertificateEnrollmentClient::init(M2MBaseList& list, const EstClient *est_client) +{ + ce_status_e ce_status = CE_STATUS_SUCCESS; + palStatus_t pal_status = PAL_SUCCESS; + + SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS(); + + if (!is_initialized) { + + // Init the LWM2M object and resource and push the object + ce_status = init_objects(list); + SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), ce_status, "Error initializing LWM2M object and resource"); + + // Put the handler creation in a critical code block for the case that this function is called after the start of the event loop + eventOS_scheduler_mutex_wait(); + if (handler_id == -1) { // Register the handler only if it hadn't been registered before + handler_id = eventOS_event_handler_create(CertificateEnrollmentClient::event_handler, EVENT_TYPE_INIT); + SA_PV_ERR_RECOVERABLE_RETURN_IF((handler_id == -1), ce_status, "Error creating event handler"); + } + eventOS_scheduler_mutex_release(); + + // Initialize the CE module + ce_status = ce_init(); + SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), ce_status, "Error initializing CE module"); + + // Create the certificate renewal mutex + pal_status = pal_osSemaphoreCreate(NUMBER_OF_CONCURRENT_RENEWALS, &g_renewal_sem); + SA_PV_ERR_RECOVERABLE_RETURN_IF((pal_status != PAL_SUCCESS), CE_STATUS_ERROR, "Error creating semaphore"); + +#ifdef CERT_ENROLLMENT_EST_MOCK + PV_UNUSED_PARAM(est_client); + g_est_client = new EstClientMock(); + SA_PV_ERR_RECOVERABLE_RETURN_IF((!g_est_client), CE_STATUS_ERROR, "Error creating mock EST"); +#else + g_est_client = est_client; +#endif + + is_initialized = true; + } + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); + return CE_STATUS_SUCCESS; +} + +void CertificateEnrollmentClient::finalize() +{ + palStatus_t pal_status; + // If module not initialized - do nothing + if (is_initialized) { + pal_status = pal_osSemaphoreDelete(&g_renewal_sem); + if (pal_status != PAL_SUCCESS) { + SA_PV_LOG_ERR("Error deleting semaphore"); + } + +#ifdef CERT_ENROLLMENT_EST_MOCK + delete g_est_client; +#endif + is_initialized = false; + + // LWM2M objects, instances, and resources are deleted when MbedCloudClient is unregistered and ServiceClient::state_unregister() is called + // Currently nothing to finalize for CE core module except for KCM. However we do not wish to finalize it it may be used by other resources + + // Release our resources + release_objects(); + } +} + +void CertificateEnrollmentClient::event_handler(arm_event_s* event) +{ + SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS(); + + switch (event->event_type) { + case EVENT_TYPE_INIT: + // Nothing to do - ce module already initialized + break; + case EVENT_TYPE_RENEWAL_REQUEST: + certificate_renewal_start(current_cert); + break; + case EVENT_TYPE_EST_RESPONDED: + est_response_process(current_cert); + break; + default: + // Should never happen + SA_PV_LOG_ERR("Unsuupported event\n"); + } + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); +} + +// This is the entry point of the renewal request, inside the event loop +void CertificateEnrollmentClient::certificate_renewal_start(CertificateRenewalDataBase *renewal_data) +{ + ce_status_e ce_status; + est_status_e est_status; + const char *cert_name; + size_t cert_name_size; + kcm_status_e kcm_status; + SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS(); + + // Parse the certificate name + ce_status = renewal_data->parse(); + SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), certificate_renewal_finish(renewal_data, ce_status), "Parse error"); + + // Create CSR's key handle + kcm_status = cs_ec_key_new(&renewal_data->key_handle); + + // translate error to some CE native error + ce_status = ce_error_handler(kcm_status); + SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), certificate_renewal_finish(renewal_data, ce_status), "Failed creating new key handle"); + + // key handle is initialized in the base constructor + ce_status = ce_generate_keys_and_create_csr_from_certificate(renewal_data->cert_name, renewal_data->key_handle, &renewal_data->csr, &renewal_data->csr_size); + SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), certificate_renewal_finish(renewal_data, ce_status), "Keys/CSR generation error"); + + // Call the EST client + + // If lwm2m device certificate - set cert name to NULL and request EST enrollment + if (pv_str_equals(g_lwm2m_name, renewal_data->cert_name,(uint32_t)(strlen(g_lwm2m_name) + 1))) { + SA_PV_LOG_INFO("Attempting to renew LwM2M device certificate\n"); + cert_name = NULL; + cert_name_size = 0; + } else { + SA_PV_LOG_INFO("Attempting to renew a custom certificate\n"); + cert_name = renewal_data->cert_name; + cert_name_size = strlen(renewal_data->cert_name); + } + + // Request a certificate from a CSR via the EST service + est_status = g_est_client->est_request_enrollment(cert_name, cert_name_size, renewal_data->csr, renewal_data->csr_size, est_cb, NULL); + // FIXME: Currently commented out. If we find that the CSR must be persistent only during est_request_enrollment call - uncomment, and this should be the only place we free the CSR + //free(renewal_data->csr); + SA_PV_ERR_RECOVERABLE_RETURN_IF((est_status != EST_STATUS_SUCCESS), certificate_renewal_finish(renewal_data, CE_STATUS_EST_ERROR), "EST request failed"); + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); +} + +ce_status_e CertificateEnrollmentClient::schedule_event(event_type_e event_type) +{ + int8_t event_status; + + arm_event_s event = { + .receiver = handler_id, // ID we got when creating our handler + .sender = 0, // Which tasklet sent us the event is irrelevant to us + .event_type = event_type, // Indicate event type + .event_id = 0, // We currently do not need an ID for a specific event - event type is enough + .data_ptr = 0, // Not needed, data handled in internal structure + .priority = ARM_LIB_LOW_PRIORITY_EVENT, // Application level priority + .event_data = 0, // With one certificate this is irrelevant. If allow multiple certificates, This will be a certificate descriptor (index in a CertificateRenewalDataBase list) + }; + + SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS(); + + event_status = eventOS_event_send(&event); + SA_PV_ERR_RECOVERABLE_RETURN_IF((event_status < 0), CE_STATUS_OUT_OF_MEMORY, "Error scheduling event"); + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); + return CE_STATUS_SUCCESS; +} + +void CertificateEnrollmentClient::est_cb(est_enrollment_result_e result, + cert_chain_context_s *cert_chain, + void *context) +{ + ce_status_e status; + SA_PV_LOG_INFO_FUNC_ENTER("result = %d", result); + + PV_UNUSED_PARAM(context); + if (result != EST_ENROLLMENT_SUCCESS || cert_chain == NULL) { + return certificate_renewal_finish(current_cert, CE_STATUS_EST_ERROR); + } + + // Cert chain remains persistent until g_est_client->free_cert_chain_context is called + current_cert->est_data = cert_chain; + + status = schedule_event(CertificateEnrollmentClient::EVENT_TYPE_EST_RESPONDED); + if (status != CE_STATUS_SUCCESS) { // If event scheduling fails - free the chain context and finish the process + SA_PV_LOG_INFO("Error scheduling event"); + g_est_client->free_cert_chain_context(current_cert->est_data); + + // Make sure we do not keep an invalid pointer + current_cert->est_data = NULL; + certificate_renewal_finish(current_cert, status); + } + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); +} + +void CertificateEnrollmentClient::est_response_process(CertificateRenewalDataBase *renewal_data) +{ + ce_status_e ce_status; + ce_renewal_params_s params; + SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS(); + + // Fill params + params.cert_data = renewal_data->est_data; + params.crypto_handle = renewal_data->key_handle; + + // Perform a safe renewal + ce_status = ce_safe_renewal(renewal_data->cert_name, ¶ms); + + // Free the est chain. Do not free in the destructor, we'd rather free it as soon as possible + g_est_client->free_cert_chain_context(renewal_data->est_data); + renewal_data->est_data = NULL; + + // Done! + certificate_renewal_finish(renewal_data, ce_status); + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); +} + +void CertificateEnrollmentClient::certificate_renewal_finish(CertificateRenewalDataBase *renewal_data, ce_status_e exit_status) +{ + palStatus_t pal_status; + SA_PV_LOG_INFO_FUNC_ENTER("exit_status = %d", exit_status); + + // Don't leave an invalid global pointer + current_cert = NULL; + + // Note: release of the mutex is before the deletion of the object (which holds the allocated cert_name) + // and before the user callback is invoked (so that the user may call the renewal API successfully from within his callback) + pal_status = pal_osSemaphoreRelease(g_renewal_sem); + if (PAL_SUCCESS != pal_status) { // + exit_status = CE_STATUS_ERROR; + } + + // At this point, new device requests may be made and the global pointer CertificateEnrollmentClient::current_cert may be changed. + // Therefore, we use the renewal_data pointer that was past as a parameter to this function + // New server requests will not be made until after this function returns since the response to the server is enqueued into the event loop by renewal_data->finish() + // and it is guaranteed that the server will not send another request until it receives a response. + renewal_data->finish(exit_status); + + delete renewal_data; + + SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS(); +} + +#ifdef CERT_RENEWAL_TEST +void CertificateEnrollmentClient::testonly_certificate_renewal_post(void *arg) +{ + return certificate_renewal_post(arg); +} + + +#endif // CERT_RENEWAL_TEST