Mbed Cloud example program for workshop in W27 2018.

Dependencies:   MMA7660 LM75B

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers m2mconnectionsecuritypimpl.cpp Source File

m2mconnectionsecuritypimpl.cpp

00001 /*
00002  * Copyright (c) 2015 - 2017 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * 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, WITHOUT
00012  * 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 // Needed for PRIu64 on FreeRTOS
00018 #include <stdio.h>
00019 // Note: this macro is needed on armcc to get the the limit macros like UINT16_MAX
00020 #ifndef __STDC_LIMIT_MACROS
00021 #define __STDC_LIMIT_MACROS
00022 #endif
00023 
00024 // Note: this macro is needed on armcc to get the the PRI*32 macros
00025 // from inttypes.h in a C++ code.
00026 #ifndef __STDC_FORMAT_MACROS
00027 #define __STDC_FORMAT_MACROS
00028 #endif
00029 
00030 #include "mbed-client/m2mconnectionhandler.h"
00031 #include "mbed-client-mbedtls/m2mconnectionsecuritypimpl.h"
00032 #include "mbed-client/m2msecurity.h"
00033 #include "mbed-trace/mbed_trace.h"
00034 #include "pal.h"
00035 #include "m2mdevice.h"
00036 #include "m2minterfacefactory.h"
00037 #include <string.h>
00038 
00039 #define TRACE_GROUP "mClt"
00040 
00041 M2MConnectionSecurityPimpl::M2MConnectionSecurityPimpl(M2MConnectionSecurity::SecurityMode mode)
00042     :_init_done(M2MConnectionSecurityPimpl::INIT_NOT_STARTED),
00043      _conf(0),
00044      _ssl(0),
00045      _sec_mode(mode)
00046 {
00047     memset(&_entropy, 0, sizeof(entropy_cb));
00048     memset(&_tls_socket, 0, sizeof(palTLSSocket_t));
00049 }
00050 
00051 M2MConnectionSecurityPimpl::~M2MConnectionSecurityPimpl()
00052 {
00053     pal_freeTLS(&_ssl);
00054     pal_tlsConfigurationFree(&_conf);
00055 }
00056 
00057 void M2MConnectionSecurityPimpl::reset()
00058 {
00059     pal_freeTLS(&_ssl);
00060     pal_tlsConfigurationFree(&_conf);
00061     _init_done = M2MConnectionSecurityPimpl::INIT_NOT_STARTED;
00062 }
00063 
00064 int M2MConnectionSecurityPimpl::init(const M2MSecurity *security, uint16_t security_instance_id)
00065 {
00066     tr_debug("M2MConnectionSecurityPimpl::init");
00067 
00068     if(!security){
00069         tr_error("M2MConnectionSecurityPimpl Security NULL.");
00070         return -1;
00071     }
00072 
00073     if(_entropy.entropy_source_ptr) {
00074         if(PAL_SUCCESS != pal_addEntropySource(_entropy.entropy_source_ptr)){
00075             return -1;
00076         }
00077     }
00078 
00079     palTLSTransportMode_t mode = PAL_DTLS_MODE;
00080     if(_sec_mode == M2MConnectionSecurity::TLS){
00081         mode = PAL_TLS_MODE;
00082     }
00083 
00084     if(PAL_SUCCESS != pal_initTLSConfiguration(&_conf, mode)){
00085         tr_error("pal_initTLSConfiguration failed");
00086         return -1;
00087     }
00088 
00089     _init_done = M2MConnectionSecurityPimpl::INIT_CONFIGURING;
00090 
00091 
00092     if(_sec_mode == M2MConnectionSecurity::DTLS){
00093         // PAL divides the defined MAX_TIMEOUT by 2
00094         pal_setHandShakeTimeOut(_conf, MBED_CLIENT_DTLS_PEER_MAX_TIMEOUT*2);
00095     }
00096 
00097     M2MSecurity::SecurityModeType cert_mode =
00098         (M2MSecurity::SecurityModeType)security->resource_value_int(M2MSecurity::SecurityMode, security_instance_id);
00099 
00100     if( cert_mode == M2MSecurity::Certificate ){
00101 
00102         palX509_t owncert;
00103         palPrivateKey_t privateKey;
00104         palX509_t caChain;
00105 
00106         // Check if we are connecting to M2MServer and check if server and device certificates are valid, no need to do this
00107         // for Bootstrap or direct LWM2M server case
00108         if ((security->server_type(security_instance_id) == M2MSecurity::M2MServer) &&
00109             (security->get_security_instance_id(M2MSecurity::Bootstrap) >= 0) &&
00110             !check_security_object_validity(security, security_instance_id)) {
00111             tr_error("M2MConnectionSecurityPimpl::init - M2MServer certificate invalid or device certificate expired!");
00112             return -1;
00113         }
00114 
00115         owncert.size = 1 + security->resource_value_buffer(M2MSecurity::PublicKey, (const uint8_t*&)owncert.buffer, security_instance_id);
00116         privateKey.size = 1 + security->resource_value_buffer(M2MSecurity::Secretkey, (const uint8_t*&)privateKey.buffer, security_instance_id);
00117         caChain.size = 1 + security->resource_value_buffer(M2MSecurity::ServerPublicKey, (const uint8_t*&)caChain.buffer, security_instance_id);
00118 
00119         if(PAL_SUCCESS != pal_setOwnCertAndPrivateKey(_conf, &owncert, &privateKey)){
00120             tr_error("pal_setOwnCertAndPrivateKey failed");
00121             return -1;
00122         }
00123         if(PAL_SUCCESS != pal_setCAChain(_conf, &caChain, NULL)){
00124             tr_error("pal_setCAChain failed");
00125             return -1;
00126         }
00127 
00128     }else if(cert_mode == M2MSecurity::Psk){
00129 
00130         uint8_t *identity = NULL;
00131         uint32_t identity_len;
00132         uint8_t *psk = NULL;
00133         uint32_t psk_len;
00134 
00135         identity_len = security->resource_value_buffer(M2MSecurity::PublicKey, identity, security_instance_id);
00136         psk_len = security->resource_value_buffer(M2MSecurity::Secretkey, psk, security_instance_id);
00137         palStatus_t ret = pal_setPSK(_conf, identity, identity_len, psk, psk_len);
00138         free(identity);
00139         free(psk);
00140 
00141         if(PAL_SUCCESS != ret){
00142             tr_error("pal_setPSK failed");
00143             return -1;
00144         }
00145 
00146     }else{
00147         tr_error("Security mode not set");
00148         return -1;
00149 
00150     }
00151 
00152     if(PAL_SUCCESS != pal_initTLS(_conf, &_ssl)){
00153         tr_error("pal_initTLS failed");
00154         return -1;
00155     }
00156 
00157     if(PAL_SUCCESS != pal_tlsSetSocket(_conf, &_tls_socket)){
00158         tr_error("pal_tlsSetSocket failed");
00159         return -1;
00160     }
00161 
00162     _init_done = M2MConnectionSecurityPimpl::INIT_DONE;
00163 
00164 #ifdef MBED_CONF_MBED_TRACE_ENABLE
00165     // Note: This call is not enough, one also needs the MBEDTLS_DEBUG_C to be defined globally
00166     // on build and if using default mbedtls configuration file, the
00167     // "#undef MBEDTLS_DEBUG_C" -line needs to be removed from mbedtls_mbed_client_config.h
00168     pal_sslDebugging(1);
00169 #endif
00170 
00171     tr_debug("M2MConnectionSecurityPimpl::init - out");
00172     return 0;
00173 }
00174 
00175 int M2MConnectionSecurityPimpl::start_handshake()
00176 {
00177     tr_debug("M2MConnectionSecurityPimpl::start_handshake");
00178 
00179     palStatus_t ret;
00180 
00181     ret = pal_handShake(_ssl, _conf);
00182 
00183     if(ret == PAL_ERR_TLS_WANT_READ || ret == PAL_ERR_TLS_WANT_WRITE || ret == PAL_ERR_TIMEOUT_EXPIRED ){
00184         return M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ;
00185     }
00186     else if(ret == PAL_ERR_TLS_PEER_CLOSE_NOTIFY) {
00187         return M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY;
00188     }
00189 
00190     if(ret != PAL_SUCCESS){ //We loose the original error here!
00191         tr_debug("M2MConnectionSecurityPimpl::start_handshake pal_handShake() error %" PRId32, ret);
00192         return -1;
00193     }
00194 
00195     ret = pal_sslGetVerifyResult(_ssl);
00196     if(PAL_SUCCESS != ret){
00197         tr_debug("M2MConnectionSecurityPimpl::start_handshake pal_sslGetVerifyResult() error %" PRId32, ret);
00198         return -1;
00199     }
00200 
00201     return ret;
00202 }
00203 
00204 int M2MConnectionSecurityPimpl::connect(M2MConnectionHandler* connHandler)
00205 {
00206     tr_debug("M2MConnectionSecurityPimpl::connect");
00207     int ret = -1;
00208 
00209     if(M2MConnectionSecurityPimpl::INIT_DONE != _init_done){
00210         return ret;
00211     }
00212 
00213     ret = start_handshake();
00214     tr_debug("M2MConnectionSecurityPimpl::connect - handshake ret: %d", ret);
00215     return ret;
00216 }
00217 
00218 
00219 int M2MConnectionSecurityPimpl::send_message(unsigned char *message, int len)
00220 {
00221     tr_debug("M2MConnectionSecurityPimpl::send_message");
00222     int ret = -1;
00223     palStatus_t return_value;
00224     uint32_t len_write;
00225 
00226     if(M2MConnectionSecurityPimpl::INIT_DONE != _init_done){
00227         return ret;
00228     }
00229 
00230 
00231     if(PAL_SUCCESS == (return_value = pal_sslWrite(_ssl, message, len, &len_write))){
00232         ret = (int)len_write;
00233     }
00234     else if(return_value == PAL_ERR_TLS_WANT_READ || return_value == PAL_ERR_TIMEOUT_EXPIRED ){
00235         ret = M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ;
00236     }
00237     else if(return_value == PAL_ERR_TLS_WANT_WRITE) {
00238         ret = M2MConnectionHandler::CONNECTION_ERROR_WANTS_WRITE;
00239     }
00240     else if(return_value == PAL_ERR_TLS_PEER_CLOSE_NOTIFY) {
00241         ret = M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY;
00242     }
00243 
00244     tr_debug("M2MConnectionSecurityPimpl::send_message - ret: %d", ret);
00245     return ret; //bytes written
00246 }
00247 
00248 int M2MConnectionSecurityPimpl::read(unsigned char* buffer, uint16_t len)
00249 {
00250     int ret = -1;
00251     palStatus_t return_value;
00252     uint32_t len_read;
00253 
00254     if(M2MConnectionSecurityPimpl::INIT_DONE != _init_done){
00255         tr_error("M2MConnectionSecurityPimpl::read - init not done!");
00256         return ret;
00257     }
00258 
00259     if(PAL_SUCCESS == (return_value = pal_sslRead(_ssl, buffer, len, &len_read))){
00260         ret = (int)len_read;
00261     }
00262 
00263     else if(return_value == PAL_ERR_TLS_WANT_READ || return_value == PAL_ERR_TLS_WANT_WRITE || return_value == PAL_ERR_TIMEOUT_EXPIRED ){
00264         ret = M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ;
00265     }
00266 
00267     else if(return_value == PAL_ERR_TLS_PEER_CLOSE_NOTIFY) {
00268         ret = M2MConnectionHandler::SSL_PEER_CLOSE_NOTIFY;
00269     }
00270 
00271     return ret;
00272 }
00273 
00274 void M2MConnectionSecurityPimpl::set_random_number_callback(random_number_cb callback)
00275 {
00276     (void)callback;
00277 }
00278 
00279 void M2MConnectionSecurityPimpl::set_entropy_callback(entropy_cb callback)
00280 {
00281 
00282     _entropy = callback;
00283 
00284 }
00285 
00286 void M2MConnectionSecurityPimpl::set_socket(palSocket_t socket, palSocketAddress_t *address)
00287 {
00288     _tls_socket.socket = socket;
00289     _tls_socket.socketAddress = address;
00290     _tls_socket.addressLength = sizeof(palSocketAddress_t);
00291 
00292     if(_sec_mode == M2MConnectionSecurity::TLS){
00293         _tls_socket.transportationMode = PAL_TLS_MODE;
00294     }
00295     else{
00296         _tls_socket.transportationMode = PAL_DTLS_MODE;
00297     }
00298 }
00299 
00300 bool M2MConnectionSecurityPimpl::certificate_parse_valid_time(const char *certificate, uint32_t certificate_len, uint64_t *valid_from, uint64_t *valid_to)
00301 {
00302     palX509Handle_t cert = 0;
00303     size_t len;
00304     palStatus_t ret;
00305 
00306     tr_debug("certificate_validfrom_time");
00307 
00308     if(PAL_SUCCESS != (ret = pal_x509Initiate(&cert))) {
00309         tr_error("certificate_validfrom_time - cert init failed: %u", (int)ret);
00310         pal_x509Free(&cert);
00311         return false;
00312     }
00313     if(PAL_SUCCESS != (ret = pal_x509CertParse(cert, (const unsigned char*)certificate, certificate_len))) {
00314         tr_error("certificate_validfrom_time - cert parse failed: %u", (int)ret);
00315         pal_x509Free(&cert);
00316         return false;
00317     }
00318     if(PAL_SUCCESS != (ret = pal_x509CertGetAttribute(cert, PAL_X509_VALID_FROM, valid_from, sizeof(uint64_t), &len))) {
00319         tr_error("certificate_validfrom_time - cert attr get failed: %u", (int)ret);
00320         pal_x509Free(&cert);
00321         return false;
00322     }
00323     if(PAL_SUCCESS != (ret = pal_x509CertGetAttribute(cert, PAL_X509_VALID_TO, valid_to, sizeof(uint64_t), &len))) {
00324         tr_error("certificate_validto_time - cert attr get failed: %u", (int)ret);
00325         pal_x509Free(&cert);
00326         return false;
00327     }
00328 
00329     pal_x509Free(&cert);
00330     return true;
00331 }
00332 
00333 bool M2MConnectionSecurityPimpl::check_security_object_validity(const M2MSecurity *security, uint16_t security_instance_id) {
00334     // Get time from device object
00335     M2MDevice *device = M2MInterfaceFactory::create_device();
00336     const uint8_t *certificate = NULL;
00337     int64_t device_time = 0;
00338     uint32_t cert_len = 0;
00339 
00340     if (device == NULL || security == NULL) {
00341         tr_error("No time from device object or security object available, fail connector registration %p, %p\n", device, security);
00342         return false;
00343     }
00344 
00345     // Get time from device object, returns -1 if resource not found
00346     device_time = device->resource_value_int(M2MDevice::CurrentTime, 0);
00347 
00348     tr_debug("Checking client certificate validity");
00349 
00350     // Get client certificate
00351     cert_len = security->resource_value_buffer(M2MSecurity::PublicKey, certificate, security_instance_id);
00352     if (cert_len == 0 || certificate == NULL) {
00353         tr_error("No certificate to check, return fail");
00354         return false;
00355     }
00356 
00357     if (device_time == -1 || !check_certificate_validity(certificate, cert_len, device_time)) {
00358         tr_error("Client certificate not valid!");
00359         return false;
00360     }
00361     return true;
00362 }
00363 
00364 bool M2MConnectionSecurityPimpl::check_certificate_validity(const uint8_t *cert, const uint32_t cert_len, const int64_t device_time)
00365 {
00366 
00367     // Get the validFrom and validTo fields from certificate
00368     uint64_t server_validfrom = 0;
00369     uint64_t server_validto = 0;
00370     if(!certificate_parse_valid_time((const char*)cert, cert_len, &server_validfrom, &server_validto)) {
00371         tr_error("Certificate time parsing failed");
00372         return false;
00373     }
00374 
00375     tr_debug("M2MConnectionSecurityPimpl::check_certificate_validity - valid from: %" PRIu64, server_validfrom);
00376     tr_debug("M2MConnectionSecurityPimpl::check_certificate_validity - valid to: %" PRIu64, server_validto);
00377     // Cast to uint32_t since all platforms does not support PRId64 macro
00378     tr_debug("M2MConnectionSecurityPimpl::check_certificate_validity - device time: %" PRIu32, (uint32_t)device_time);
00379 
00380     if (device_time < (uint32_t)server_validfrom || device_time > (uint32_t)server_validto) {
00381         tr_error("Invalid certificate validity or device time outside of certificate validity period!");
00382         return false;
00383     }
00384 
00385     return true;
00386 }
00387