Simple interface for Mbed Cloud Client
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Jul 12 2022 19:01:35 by 1.7.2