Simple interface for Mbed Cloud Client

Dependents:  

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