Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers CertificateEnrollmentClient.cpp Source File

CertificateEnrollmentClient.cpp

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2018 ARM Ltd.
00003 //  
00004 // Licensed under the Apache License, Version 2.0 (the "License");
00005 // you may not use this file except in compliance with the License.
00006 // You may obtain a copy of the License at
00007 //  
00008 //     http://www.apache.org/licenses/LICENSE-2.0
00009 //  
00010 // Unless required by applicable law or agreed to in writing, software
00011 // distributed under the License is distributed on an "AS IS" BASIS,
00012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013 // See the License for the specific language governing permissions and
00014 // limitations under the License.
00015 // ----------------------------------------------------------------------------
00016 
00017 
00018 #include "CertificateEnrollmentClient.h"
00019 #include "mbed-client/m2mresource.h"
00020 #include "mbed-client/m2minterfacefactory.h"
00021 #include "mbed-client/m2minterface.h"
00022 #include "pal.h"
00023 #include "eventOS_scheduler.h"
00024 #include "eventOS_event.h"
00025 #include "ce_defs.h"
00026 #include "ce_tlv.h"
00027 #include "certificate_enrollment.h"
00028 #include <stdio.h>
00029 #include <string.h>
00030 #include "CertificateEnrollmentClient.h"
00031 #include "CertificateEnrollmentClientCommon.h"
00032 #include "CertificateRenewalData.h"
00033 #include "pv_error_handling.h"
00034 #include "pv_macros.h"
00035 
00036 #define RESOURCE_ID_CERTIFICATE_NAME "27002"
00037 #define OBJECT_LWM2M_CERTIFICATE "35011"
00038 
00039 #define NUMBER_OF_CONCURRENT_RENEWALS 1
00040 
00041 
00042 /************************************************************************/
00043 /* Different calls to update                                            */
00044 /************************************************************************/
00045 
00046 extern const char g_lwm2m_name[];
00047 
00048 namespace CertificateEnrollmentClient {
00049 
00050     // Event type that is part of the arm_event_s structure.
00051     enum event_type_e {
00052         EVENT_TYPE_INIT, // Some initializer - nothing done currently, initialization called by mbed cloud client
00053         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
00054         EVENT_TYPE_EST_RESPONDED, // Certificate arrived from EST service, or EST failure
00055         EVENT_TYPE_MAX = 0xff // Must fit in a uint8_t (field in the arm_event_s struct)
00056     };
00057 
00058     // Pointer to the EST client object for dealing with the EST service.
00059     extern const CERT_ENROLLMENT_EST_CLIENT *g_est_client;
00060 
00061     // Data for certificate that is currently being renewed. Will change to list if we need to support multiple renewals
00062     static CertificateRenewalDataBase *current_cert = NULL;
00063 
00064     // ID of the handler we register to the MbedCloudClient event loop
00065     static int8_t handler_id = -1;
00066 
00067     // Flag that indicates whether the module is initialized
00068     static bool is_initialized = false;
00069 
00070     // Semaphore for enforcing that only NUMBER_OF_CONCURRENT_RENEWALS (currently 1) request at a time may update current_cert. Hold lock until process finished.
00071     // Important: When pal_osSemaphoreWait called from within event loop - do not block, must set timeout to 0, and fail if failed to acquire lock
00072     // Future: For supporting renewals of NUMBER_OF_CONCURRENT_RENEWALS certificates simultaneously - change to semaphore that counts to NUMBER_OF_CONCURRENT_RENEWALS
00073     //         and maintain a list of certificates of size NUMBER_OF_CONCURRENT_RENEWALS inside the event loop.
00074     static palSemaphoreID_t g_renewal_sem = 0;
00075 
00076 
00077     /**
00078     * \brief Finish the renewal process.
00079     * Zero current_cert pointer, then release the semaphore. Note that when the semaphore is released - new device renewals may be made.
00080     * Then call renewal_data->finish() and delete renewal_data.
00081     *
00082     * \param renewal_data the data of the certificate to be renewed. 
00083     *        It is important that this is passed to the function because after releasing the semaphore - the global pointer may be replaced.
00084     * \param exit_status the status of the renewal process
00085     */
00086     static void certificate_renewal_finish(CertificateRenewalDataBase *renewal_data, ce_status_e  exit_status);
00087 
00088     /**
00089     * \brief The function that handles all the CertificateEnrollmentClient events
00090     * Create an arm_event_s object and call eventOS_event_send()
00091     * The event will have an application level priority, and will be executed when in the head of the event queue.
00092     * In the future: an extra arg should be passed - some descriptor for the specific CertificateRenewalDataBase object.
00093     *
00094     * \param event_type An event identifier
00095     */
00096     static void event_handler(arm_event_s* event);
00097 
00098     /**
00099     * \brief Send a new event to the event loop queue.
00100     * Create an arm_event_s object and call eventOS_event_send()
00101     * The event will have an application level priority
00102     *
00103     * \param renewal_data A pointer to an object derived from CertificateRenewalDataBase
00104     * \param event_type An event identifier
00105     */
00106     static ce_status_e  schedule_event(event_type_e event_type);
00107 
00108     /**
00109     * \brief Callback that will be executed when an EST service response is available
00110     * 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.
00111     *
00112     * \param result Whether the EST client successfully received a certificate from the EST service
00113     * \param cert_chain structure containing the certificate/chain received from the EST service
00114     * \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.
00115     */
00116     static void est_cb(est_enrollment_result_e result,
00117                        cert_chain_context_s *cert_chain,
00118                        void *context);
00119 
00120     /**
00121     * \brief The function that handles the EST response.
00122     *
00123     * Called by event_handler(). renewal_data->est_data already exists and is valid.
00124     * Will perform a safe replacement of the certificate with the new certificate received from EST
00125     * Then it will free the EST chain and finish the renewal operation.
00126     * \param renewal_data A pointer to an object derived from CertificateRenewalDataBase
00127     */
00128     static void est_response_process(CertificateRenewalDataBase *renewal_data);
00129 
00130     /**
00131     * \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
00132     *
00133     * 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()
00134     * \param list A reference to the MbedCloudClient object list. MbedCloudClient will later set the resource
00135     */
00136     static ce_status_e  init_objects(M2MBaseList& list);
00137 
00138     /**
00139     * \brief Release the objects created by init_objects()
00140     *
00141     */
00142     static void release_objects();
00143 
00144     // Callback is called when we get a POST message to g_cert_enroll_lwm2m_resource (runs in high priority context!)
00145     /**
00146     * \brief Callback is called when we get a POST message to g_cert_enroll_lwm2m_resource
00147     * Runs in network context of the event loop.
00148     * This function extracts the input data, creates a CertificateEnrollmentClient::CertificateRenewalDataFromServer object sets the global variable
00149     *
00150     * \param arg a M2MResource::M2MExecuteParameter argument.
00151     */
00152     static void certificate_renewal_post(void *arg);
00153 
00154     /**
00155     * \brief Start the renewal process.
00156     * Parse the certificate name, generate keys and CSR. Then call the EST client so the new certificate may be retrieved
00157     *
00158     * \param renewal_data the data of the certificate to be renewed 
00159     */
00160     static void certificate_renewal_start(CertificateRenewalDataBase *renewal_data);
00161 
00162     /**
00163     * \brief Call the user callback and send a response to the server, when a CertificateRenewalDataFromServer object does not exist.
00164     * Use only for server initiated renewal, since this sends a response to the server.
00165     *
00166     * \param tlv the raw data from the server - should be in the form of TLV
00167     * \param tlv_size size of the TLV buffer
00168     * \param ret_status The return status to return to the user callback and the server
00169     */
00170     static void call_user_cb_send_response(const uint8_t *tlv, uint16_t tlv_size, ce_status_e  ret_status);
00171 
00172 #ifdef CERT_RENEWAL_TEST
00173     void testonly_certificate_renewal_post(void *arg);
00174 #endif // CERT_RENEWAL_TEST
00175 
00176 }
00177 
00178 // FIXME: print error
00179 void CertificateEnrollmentClient::call_user_cb_send_response(const uint8_t *tlv, uint16_t tlv_size, ce_status_e  ret_status)
00180 {
00181     CertificateEnrollmentClient::CertificateRenewalDataFromServer temp_obj(tlv, tlv_size);
00182     (void)temp_obj.parse();
00183 
00184     // Call user callback with appropriate error
00185     // In case of parsing error (malformed TLV), the provided ret_status will be returned and not 
00186     call_user_cert_renewal_cb(temp_obj.cert_name, ret_status, CE_INITIATOR_SERVER);
00187 
00188     // Send response to the server
00189     SA_PV_LOG_INFO("sending delayed response\n");
00190     g_cert_enroll_lwm2m_resource->set_value(ret_status);
00191     g_cert_enroll_lwm2m_resource->send_delayed_post_response();
00192 }
00193 
00194 void CertificateEnrollmentClient::certificate_renewal_post(void *arg)
00195 {
00196     palStatus_t pal_status;
00197     ce_status_e  status;
00198     SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS();
00199 
00200     M2MResource::M2MExecuteParameter *args = (M2MResource::M2MExecuteParameter *)arg;
00201     const uint8_t *data = args->get_argument_value();
00202     const uint16_t data_size = args->get_argument_value_length();
00203 
00204     // If CEC module is not initialized - do not even take semaphore - exit with proper response
00205     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");
00206 
00207     pal_status = pal_osSemaphoreWait(g_renewal_sem, 0, NULL);
00208 
00209     if (pal_status == PAL_SUCCESS) {
00210         CertificateEnrollmentClient::current_cert = new CertificateEnrollmentClient::CertificateRenewalDataFromServer(data, data_size);
00211         if (!CertificateEnrollmentClient::current_cert) {
00212             status = CE_STATUS_OUT_OF_MEMORY;
00213             pal_status = pal_osSemaphoreRelease(g_renewal_sem);
00214             if (PAL_SUCCESS != pal_status) { // Should never happen
00215                 status = CE_STATUS_ERROR;
00216             }
00217 
00218             call_user_cb_send_response(data, data_size, status);
00219             return;
00220         }
00221 
00222         // Enqueue the event
00223         status = schedule_event(CertificateEnrollmentClient::EVENT_TYPE_RENEWAL_REQUEST);
00224         SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_STATUS_SUCCESS), certificate_renewal_finish(CertificateEnrollmentClient::current_cert, status), "Error scheduling event");
00225 
00226     } else {
00227         SA_PV_LOG_ERR("Failed to take semaphore- device busy\n");
00228 
00229         if (pal_status == PAL_ERR_RTOS_TIMEOUT ) {
00230             status = CE_STATUS_DEVICE_BUSY;
00231         } else {
00232             status = CE_STATUS_ERROR;
00233         }
00234 
00235         call_user_cb_send_response(data, data_size, status);
00236         return;
00237     }
00238 
00239     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00240 }
00241 
00242 ce_status_e  CertificateEnrollmentClient::certificate_renew(const char *cert_name)
00243 {
00244     palStatus_t pal_status = PAL_SUCCESS;
00245     ce_status_e  status = CE_STATUS_SUCCESS;
00246 
00247     SA_PV_ERR_RECOVERABLE_RETURN_IF((!cert_name), CE_STATUS_INVALID_PARAMETER, "Provided NULL certificate name");
00248     SA_PV_ERR_RECOVERABLE_RETURN_IF((!is_initialized), CE_STATUS_NOT_INITIALIZED, "Certificate Renewal module not initialized");
00249 
00250     SA_PV_LOG_INFO_FUNC_ENTER("cert_name = %s\n", cert_name);
00251 
00252     pal_status = pal_osSemaphoreWait(g_renewal_sem, 0, NULL);
00253 
00254     if (pal_status == PAL_SUCCESS) {
00255         CertificateEnrollmentClient::current_cert = new CertificateEnrollmentClient::CertificateRenewalDataFromDevice(cert_name);
00256         SA_PV_ERR_RECOVERABLE_GOTO_IF((!CertificateEnrollmentClient::current_cert), status = CE_STATUS_OUT_OF_MEMORY, ReleseSemReturn, "Allocation error");
00257 
00258         // Enqueue the event
00259         status = schedule_event(CertificateEnrollmentClient::EVENT_TYPE_RENEWAL_REQUEST);
00260         SA_PV_ERR_RECOVERABLE_GOTO_IF((status != CE_STATUS_SUCCESS), status = status, ReleseSemReturn, "Error scheduling event");
00261 
00262         // If some error synchronous error has occurred before scheduling the event - release the semaphore we had just taken, 
00263         // and then return the error without calling the user callback 
00264 ReleseSemReturn:
00265         if (status != CE_STATUS_SUCCESS) {
00266             pal_status = pal_osSemaphoreRelease(g_renewal_sem);
00267             if (PAL_SUCCESS != pal_status) { // Should never happen
00268                 status = CE_STATUS_ERROR;
00269             }
00270         }
00271 
00272     } else {
00273         // return with appropriate error
00274         if (pal_status == PAL_ERR_RTOS_TIMEOUT ) {
00275             status = CE_STATUS_DEVICE_BUSY;
00276         } else {
00277             status = CE_STATUS_ERROR;
00278         }
00279         
00280     }
00281 
00282     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00283     return status;
00284 }
00285 
00286 
00287 void CertificateEnrollmentClient::on_certificate_renewal(cert_renewal_cb_f user_cb)
00288 {
00289     SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS();
00290     CertificateEnrollmentClient::set_user_cert_renewal_cb(user_cb);
00291     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00292 }
00293 
00294 
00295 ce_status_e  CertificateEnrollmentClient::init_objects(M2MBaseList& list)
00296 {
00297     M2MObjectInstance *cert_enroll_lwm2m_obj_instance;
00298 
00299     ce_status_e  ce_status = CE_STATUS_SUCCESS;
00300     SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS();
00301 
00302     // Create the certificate enrollment resource
00303     g_cert_enroll_lwm2m_obj = M2MInterfaceFactory::create_object(OBJECT_LWM2M_CERTIFICATE);
00304     SA_PV_ERR_RECOVERABLE_RETURN_IF((!g_cert_enroll_lwm2m_obj), CE_STATUS_ERROR, "Error creating LWM2M object");
00305 
00306     // Create the instance
00307     cert_enroll_lwm2m_obj_instance = g_cert_enroll_lwm2m_obj->create_object_instance();
00308     SA_PV_ERR_RECOVERABLE_GOTO_IF((!cert_enroll_lwm2m_obj_instance), ce_status = CE_STATUS_ERROR, Cleanup, "Error creating LWM2M object instance");
00309 
00310     // Create the resource
00311     g_cert_enroll_lwm2m_resource = cert_enroll_lwm2m_obj_instance->create_dynamic_resource(RESOURCE_ID_CERTIFICATE_NAME, "Enroll", M2MResourceInstance::INTEGER, false);
00312     SA_PV_ERR_RECOVERABLE_GOTO_IF((!g_cert_enroll_lwm2m_resource), ce_status = CE_STATUS_ERROR, Cleanup, "Error creating LWM2M resource");
00313 
00314     // Allow POST operations
00315     g_cert_enroll_lwm2m_resource->set_operation(M2MBase::POST_ALLOWED);
00316 
00317     // Set the resource callback
00318     SA_PV_ERR_RECOVERABLE_GOTO_IF((!g_cert_enroll_lwm2m_resource->set_execute_function(CertificateEnrollmentClient::certificate_renewal_post)),
00319                                   ce_status = CE_STATUS_ERROR, Cleanup, "Error resource callback");
00320 
00321     // Enable sending of delayed responses
00322     g_cert_enroll_lwm2m_resource->set_delayed_response(true);
00323 
00324     // Push the object to the list
00325     list.push_back(g_cert_enroll_lwm2m_obj);
00326 
00327 Cleanup:
00328     if (ce_status != CE_STATUS_SUCCESS) {
00329         // Destroying the object will destroy all instances and resources associated with it
00330         delete g_cert_enroll_lwm2m_obj;
00331         g_cert_enroll_lwm2m_resource = NULL;
00332     }
00333 
00334     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00335     return ce_status;
00336 }
00337 
00338 void CertificateEnrollmentClient::release_objects()
00339 {
00340     delete g_cert_enroll_lwm2m_obj;
00341     g_cert_enroll_lwm2m_obj = NULL;
00342 }
00343 
00344 
00345 ce_status_e  CertificateEnrollmentClient::init(M2MBaseList& list, const EstClient *est_client)
00346 {
00347     ce_status_e  ce_status = CE_STATUS_SUCCESS;
00348     palStatus_t pal_status = PAL_SUCCESS;
00349 
00350     SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS();
00351 
00352     if (!is_initialized) {
00353 
00354         // Init the LWM2M object and resource and push the object
00355         ce_status = init_objects(list);
00356         SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), ce_status, "Error initializing LWM2M object and resource");
00357 
00358         // Put the handler creation in a critical code block for the case that this function is called after the start of the event loop
00359         eventOS_scheduler_mutex_wait();
00360         if (handler_id == -1) { // Register the handler only if it hadn't been registered before
00361             handler_id = eventOS_event_handler_create(CertificateEnrollmentClient::event_handler, EVENT_TYPE_INIT);
00362             SA_PV_ERR_RECOVERABLE_RETURN_IF((handler_id == -1), ce_status, "Error creating event handler");
00363         }
00364         eventOS_scheduler_mutex_release();
00365 
00366         // Initialize the CE module
00367         ce_status = ce_init();
00368         SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), ce_status, "Error initializing CE module");
00369 
00370         // Create the certificate renewal mutex
00371         pal_status = pal_osSemaphoreCreate(NUMBER_OF_CONCURRENT_RENEWALS, &g_renewal_sem);
00372         SA_PV_ERR_RECOVERABLE_RETURN_IF((pal_status != PAL_SUCCESS), CE_STATUS_ERROR, "Error creating semaphore");
00373 
00374 #ifdef CERT_ENROLLMENT_EST_MOCK
00375         PV_UNUSED_PARAM(est_client);
00376         g_est_client = new EstClientMock();        
00377         SA_PV_ERR_RECOVERABLE_RETURN_IF((!g_est_client), CE_STATUS_ERROR, "Error creating mock EST");
00378 #else 
00379         g_est_client = est_client;
00380 #endif
00381 
00382         is_initialized = true;
00383     }
00384 
00385     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00386     return CE_STATUS_SUCCESS;
00387 }
00388 
00389 void CertificateEnrollmentClient::finalize()
00390 {
00391     palStatus_t pal_status;
00392     // If module not initialized - do nothing
00393     if (is_initialized) {
00394         pal_status = pal_osSemaphoreDelete(&g_renewal_sem);
00395         if (pal_status != PAL_SUCCESS) {
00396             SA_PV_LOG_ERR("Error deleting semaphore");
00397         }
00398 
00399 #ifdef CERT_ENROLLMENT_EST_MOCK
00400         delete g_est_client;
00401 #endif
00402         is_initialized = false;
00403 
00404         // LWM2M objects, instances, and resources are deleted when MbedCloudClient is unregistered and ServiceClient::state_unregister() is called
00405         // 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
00406         
00407         // Release our resources
00408         release_objects();
00409     }
00410 }
00411 
00412 void CertificateEnrollmentClient::event_handler(arm_event_s* event)
00413 {
00414     SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS();
00415 
00416     switch (event->event_type) {
00417         case EVENT_TYPE_INIT:
00418             // Nothing to do - ce module already initialized
00419             break;
00420         case EVENT_TYPE_RENEWAL_REQUEST:
00421             certificate_renewal_start(current_cert);
00422             break;
00423         case EVENT_TYPE_EST_RESPONDED:
00424             est_response_process(current_cert);
00425             break;
00426         default:
00427             // Should never happen
00428             SA_PV_LOG_ERR("Unsuupported event\n");
00429     }
00430 
00431     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00432 }
00433 
00434 // This is the entry point of the renewal request, inside the event loop
00435 void CertificateEnrollmentClient::certificate_renewal_start(CertificateRenewalDataBase *renewal_data)
00436 {
00437     ce_status_e  ce_status;
00438     est_status_e est_status;
00439     const char *cert_name;
00440     size_t cert_name_size;
00441     kcm_status_e  kcm_status;
00442     SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS();
00443 
00444     // Parse the certificate name
00445     ce_status = renewal_data->parse();
00446     SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), certificate_renewal_finish(renewal_data, ce_status), "Parse error");
00447 
00448     // Create CSR's key handle
00449     kcm_status = cs_ec_key_new(&renewal_data->key_handle);
00450 
00451     // translate error to some CE native error
00452     ce_status = ce_error_handler(kcm_status);
00453     SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), certificate_renewal_finish(renewal_data, ce_status), "Failed creating new key handle");
00454 
00455     // key handle is initialized in the base constructor
00456     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);
00457     SA_PV_ERR_RECOVERABLE_RETURN_IF((ce_status != CE_STATUS_SUCCESS), certificate_renewal_finish(renewal_data, ce_status), "Keys/CSR generation error");
00458 
00459     // Call the EST client
00460 
00461     // If lwm2m device certificate - set cert name to NULL and request EST enrollment
00462     if (pv_str_equals(g_lwm2m_name, renewal_data->cert_name,(uint32_t)(strlen(g_lwm2m_name) + 1))) {
00463         SA_PV_LOG_INFO("Attempting to renew LwM2M device certificate\n");
00464         cert_name = NULL;
00465         cert_name_size = 0;
00466     } else {
00467         SA_PV_LOG_INFO("Attempting to renew a custom certificate\n");
00468         cert_name = renewal_data->cert_name;
00469         cert_name_size = strlen(renewal_data->cert_name);
00470     }
00471 
00472     // Request a certificate from a CSR via the EST service
00473     est_status = g_est_client->est_request_enrollment(cert_name, cert_name_size, renewal_data->csr, renewal_data->csr_size, est_cb, NULL);
00474     // 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
00475     //free(renewal_data->csr);
00476     SA_PV_ERR_RECOVERABLE_RETURN_IF((est_status != EST_STATUS_SUCCESS), certificate_renewal_finish(renewal_data, CE_STATUS_EST_ERROR), "EST request failed");
00477 
00478     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00479 }
00480 
00481 ce_status_e  CertificateEnrollmentClient::schedule_event(event_type_e event_type)
00482 {
00483     int8_t event_status;
00484 
00485     arm_event_s event = {
00486         .receiver = handler_id, // ID we got when creating our handler
00487         .sender = 0, // Which tasklet sent us the event is irrelevant to us 
00488         .event_type = event_type, // Indicate event type 
00489         .event_id = 0, // We currently do not need an ID for a specific event - event type is enough
00490         .data_ptr = 0, // Not needed, data handled in internal structure
00491         .priority = ARM_LIB_LOW_PRIORITY_EVENT, // Application level priority
00492         .event_data = 0, // With one certificate this is irrelevant. If allow multiple certificates, This will be a certificate descriptor (index in a CertificateRenewalDataBase list)
00493     };
00494 
00495     SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS();
00496 
00497     event_status = eventOS_event_send(&event);
00498     SA_PV_ERR_RECOVERABLE_RETURN_IF((event_status < 0), CE_STATUS_OUT_OF_MEMORY, "Error scheduling event");
00499 
00500     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00501     return CE_STATUS_SUCCESS;
00502 }
00503 
00504 void CertificateEnrollmentClient::est_cb(est_enrollment_result_e result,
00505                                          cert_chain_context_s *cert_chain,
00506                                          void *context)
00507 {
00508     ce_status_e  status;
00509     SA_PV_LOG_INFO_FUNC_ENTER("result = %d", result);
00510 
00511     PV_UNUSED_PARAM(context);   
00512     if (result != EST_ENROLLMENT_SUCCESS || cert_chain == NULL) {
00513         return certificate_renewal_finish(current_cert, CE_STATUS_EST_ERROR);
00514     }
00515 
00516     // Cert chain remains persistent until g_est_client->free_cert_chain_context is called
00517     current_cert->est_data = cert_chain;
00518 
00519     status = schedule_event(CertificateEnrollmentClient::EVENT_TYPE_EST_RESPONDED);
00520     if (status != CE_STATUS_SUCCESS) { // If event scheduling fails - free the chain context and finish the process
00521         SA_PV_LOG_INFO("Error scheduling event");
00522         g_est_client->free_cert_chain_context(current_cert->est_data);
00523         
00524         // Make sure we do not keep an invalid pointer
00525         current_cert->est_data = NULL;
00526         certificate_renewal_finish(current_cert, status);
00527     }
00528 
00529     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00530 }
00531 
00532 void CertificateEnrollmentClient::est_response_process(CertificateRenewalDataBase *renewal_data)
00533 {
00534     ce_status_e  ce_status;
00535     ce_renewal_params_s params;
00536     SA_PV_LOG_INFO_FUNC_ENTER_NO_ARGS();
00537 
00538     // Fill params
00539     params.cert_data = renewal_data->est_data;
00540     params.crypto_handle = renewal_data->key_handle;
00541 
00542     // Perform a safe renewal
00543     ce_status = ce_safe_renewal(renewal_data->cert_name, &params);
00544 
00545     // Free the est chain. Do not free in the destructor, we'd rather free it as soon as possible
00546     g_est_client->free_cert_chain_context(renewal_data->est_data);
00547     renewal_data->est_data = NULL;
00548 
00549     // Done!
00550     certificate_renewal_finish(renewal_data, ce_status);
00551     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00552 }
00553 
00554 void CertificateEnrollmentClient::certificate_renewal_finish(CertificateRenewalDataBase *renewal_data, ce_status_e  exit_status)
00555 {
00556     palStatus_t pal_status;
00557     SA_PV_LOG_INFO_FUNC_ENTER("exit_status = %d", exit_status);
00558 
00559     // Don't leave an invalid global pointer
00560     current_cert = NULL;
00561 
00562     // Note: release of the mutex is before the deletion of the object (which holds the allocated cert_name)
00563     // and before the user callback is invoked (so that the user may call the renewal API successfully from within his callback)
00564     pal_status = pal_osSemaphoreRelease(g_renewal_sem);
00565     if (PAL_SUCCESS != pal_status) { // 
00566         exit_status = CE_STATUS_ERROR;
00567     }
00568 
00569     // At this point, new device requests may be made and the global pointer CertificateEnrollmentClient::current_cert may be changed.
00570     // Therefore, we use the renewal_data pointer that was past as a parameter to this function
00571     // 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()
00572     // and it is guaranteed that the server will not send another request until it receives a response.
00573     renewal_data->finish(exit_status);
00574 
00575     delete renewal_data;
00576 
00577     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00578 }
00579 
00580 #ifdef CERT_RENEWAL_TEST
00581 void CertificateEnrollmentClient::testonly_certificate_renewal_post(void *arg)
00582 {
00583     return certificate_renewal_post(arg);
00584 }
00585 
00586 
00587 #endif // CERT_RENEWAL_TEST