EapTlsResult EapTls_RunConnectionManager()

in EAP-TLS_Solution/EAP-TLS Client/lib/eap_tls_lib.c [1359:2042]


EapTlsResult EapTls_RunConnectionManager(EapTlsConfig *eapTlsConfig)
{
	EapTlsResult iRes = EapTlsResult_Error;

	// Global variables (to state-machine)
	EapTlsConfig radiusNetwork;
	EapTlsConfig radiusNetwork_dup;
	bool requestRootCaCertificate;
	bool requestClientCertificate;

	// Variable for handling the EAP-TLS network configuration, including its duplicate (used when renewing certificates with the WebAPI).
	bool duplicatingNetwork = false;

	// Temporary storage for the RootCA & Client certificates returned from the WebAPI (use Stack or static depending on SRAM/Flash constraints)
	MemoryBlock webApiResponseBlob = { .data = NULL, .size = 0 };
	WebApiResponse *webApiResponse = NULL;

	// Check basic parameter requirements
	{
		if (NULL == eapTlsConfig)
		{
			EapTls_Log("Invalid configuration pointer.\n");
			return EapTlsResult_BadParameters;
		}
		if (NetworkInterfaceType_Undefined == eapTlsConfig->bootstrapNetworkInterfaceType)
		{
			EapTls_Log("ERROR: EapTlsConfig::bootstrapNetworkInterfaceType is undefined!\n");
			return EapTlsResult_BadParameters;
		}
		if (NULL == eapTlsConfig->bootstrapNetworkName || 0 == eapTlsConfig->bootstrapNetworkName)
		{
			EapTls_Log("ERROR: EapTlsConfig::bootstrapNetworkName is NULL or empty!\n");
			return EapTlsResult_BadParameters;
		}
		if (NULL == eapTlsConfig->bootstrapNetworkSsid || 0 == eapTlsConfig->bootstrapNetworkSsid)
		{
			EapTls_Log("ERROR: EapTlsConfig::bootstrapNetworkSsid is NULL or empty!\n");
			return EapTlsResult_BadParameters;
		}
		if (NULL == eapTlsConfig->mdmWebApiInterfaceUrl || 0 == eapTlsConfig->mdmWebApiInterfaceUrl)
		{
			EapTls_Log("ERROR: EapTlsConfig::mdmWebApiInterfaceUrl is NULL or empty!\n");
			return EapTlsResult_BadParameters;
		}
		if (NULL == eapTlsConfig->eapTlsRootCertificate.id || 0 == eapTlsConfig->eapTlsRootCertificate.id)
		{
			EapTls_Log("ERROR: EapTlsConfig::eapTlsRootCertificate.id is NULL or empty!\n");
			return EapTlsResult_BadParameters;
		}
		if (NULL == eapTlsConfig->eapTlsClientCertificate.id || 0 == eapTlsConfig->eapTlsClientCertificate.id)
		{
			EapTls_Log("ERROR: EapTlsConfig::eapTlsClientCertificate.id is NULL or empty!\n");
			return EapTlsResult_BadParameters;
		}
		if (NULL == eapTlsConfig->eapTlsClientCertificate.privateKeyPass || 0 == eapTlsConfig->eapTlsClientCertificate.privateKeyPass)
		{
			EapTls_Log("ERROR: EapTlsConfig::eapTlsClientCertificate.privateKeyPass is NULL or empty!\n");
			return EapTlsResult_BadParameters;
		}
	}

	// Clone the configurations to the library's statics
	memcpy(&radiusNetwork, eapTlsConfig, sizeof(EapTlsConfig));
	memcpy(&radiusNetwork_dup, eapTlsConfig, sizeof(EapTlsConfig));

	bool exitStateMachine = false;
	ConnectionManagerState currentState = ConnectionManagerState_Idle;
	while (!exitStateMachine)
	{
		switch (currentState)
		{
			case ConnectionManagerState_TBD: // Just a dead-end, to capture incomplete branch coding
			{
				EapTls_Log("Stuck into EAP_TLS_TBD... see call-stack!!\n");
				while (1);
			}
			break;

			case ConnectionManagerState_Idle: // Just starting off: let's check if we have RootCA and Client certificates
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_Idle\n");
				currentState = ConnectionManagerState_CheckCertsInstalled;
			}
			break;

			case ConnectionManagerState_CheckCertsInstalled: // Check if we have installed RootCA and Client certificates
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_CheckCertsInstalled\n");

				requestRootCaCertificate = (EapTlsResult_Success != EapTls_IsCertificateInstalled(radiusNetwork.eapTlsRootCertificate.id));
				requestClientCertificate = (EapTlsResult_Success != EapTls_IsCertificateInstalled(radiusNetwork.eapTlsClientCertificate.id));

				currentState = (requestRootCaCertificate || requestClientCertificate) ? ConnectionManagerState_RequestCertificates : ConnectionManagerState_ConnectToEapTlsNetwork;
			}
			break;

			case ConnectionManagerState_AddEapTlsNetwork: // Add the EAP_TLS network from scratch (eventually removes existing)
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_AddEapTlsNetwork\n");

				// Always (eventually) remove the EAP_TLS network (no return/error check required, logging already within the API)
				EapTls_RemoveNetwork(radiusNetwork.eapTlsNetworkName);

				// Let's check if we can immediately connect to the EAP_TLS server
				iRes = EapTls_AddNetwork(radiusNetwork.eapTlsNetworkName, radiusNetwork.eapTlsNetworkSsid, WifiConfig_Security_Wpa2_EAP_TLS, NULL);
				if (EapTlsResult_Success == iRes)
				{
					EapTls_Log("Successfully added new EAP-TLS network '%s'!\n", radiusNetwork.eapTlsNetworkName);

					currentState = ConnectionManagerState_ConfigureEapTlsNetwork;
					continue;
				}

				// We cannot add the network --> let's give back control to the app to decide
				iRes = EapTlsResult_FailedAddingEapTlsNetwork;
				currentState = ConnectionManagerState_Error_Exit;
			}
			break;

			case ConnectionManagerState_CloneEapTlsNetwork: // Clone the EAP_TLS network, for use in certificate renewal
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_CloneEapTlsNetwork\n");

				// Let's duplicate the configuration structures, except for the network name and certificate IDs
				radiusNetwork_dup = radiusNetwork;
				snprintf(radiusNetwork_dup.eapTlsNetworkName, sizeof(radiusNetwork_dup.eapTlsNetworkName) - 1, "_%s", radiusNetwork.eapTlsNetworkName);

				// Always (eventually) remove the old attempts of duplication of the EAP_TLS network (no return/error check required, logging already within the API)
				EapTls_RemoveNetwork(radiusNetwork_dup.eapTlsNetworkName);

				iRes = EapTls_CloneNetworkConfig(&radiusNetwork, &radiusNetwork_dup);
				if (EapTlsResult_Success != iRes)
				{
					EapTls_Log("Cannot create temporary network configuration for '%s'\n", radiusNetwork.eapTlsNetworkName);

					// We cannot attempt duplicating the network to try a different authentication path --> let's give back control to the app to decide
					iRes = EapTlsResult_FailedCloningEapTlsNetwork;
					currentState = ConnectionManagerState_Error_Exit;
				}
				else
				{
					// Let's install the certs only once we are sure we'll have a network configuration to attach them to
					currentState = ConnectionManagerState_Installcerts_Dup;
				}
			}
			break;

			// Optimizing similar state handling
			case ConnectionManagerState_Installcerts: // Install RootCA and Client certificates
			case ConnectionManagerState_Installcerts_Dup: // Install new RootCA and Client certificates, to be registered into the EAP_TLS's <TEMPORARY CLONE> network
			{
				EapTls_Log("EapTls_RunConnectionManager::%s\n", duplicatingNetwork ? "ConnectionManagerState_Installcerts_Dup" : "ConnectionManagerState_Installcerts");

				if (NULL != webApiResponse)
				{
					EapTlsConfig *network = duplicatingNetwork ? &radiusNetwork_dup : &radiusNetwork;

					// Install the RootCA certificate, if we asked for one and if it's different
					if (requestRootCaCertificate)
					{
						if (0 != EapTls_CompareCertificates((const char*)&network->eapTlsRootCertificate, webApiResponse->rootCACertficate, strlen((const char*)&network->eapTlsRootCertificate)))
						{
							iRes = EapTls_InstallRootCaCertificatePem((const char*)&network->eapTlsRootCertificate.id, webApiResponse->rootCACertficate);
							if (EapTlsResult_Success != iRes)
							{
								iRes = EapTlsResult_FailedInstallingCertificates;
								currentState = ConnectionManagerState_Error_Exit;
								continue;
							}
						}						
					}					

					// Install the Client certificate, if we asked for one and if it's different
					if (requestClientCertificate)
					{
						if (0 != EapTls_CompareCertificates((const char*)&network->eapTlsClientCertificate, webApiResponse->clientPublicCertificate, strlen((const char*)&network->eapTlsClientCertificate)))
						{
							// NOTE: it left to the customer to decide wither to use the private key password that "may" be returned from the WebAPI
							// or a password that is hard-coded into the code (and that could be updated with future App updates)
							iRes = EapTls_InstallClientCertificatePem((const char*)&network->eapTlsClientCertificate.id, 
								webApiResponse->clientPublicCertificate,
								webApiResponse->clientPrivateKey,
#ifdef USE_CLIENT_CERT_PRIVATE_KEY_PASS_FROM_WEBAPI
								webApiResponse->clientPrivateKeyPass);
#else
								network->eapTlsClientCertificate.privateKeyPass);
#endif // USE_CLIENT_CERT_PRIVATE_KEY_PASS_FROM_WEBAPI
								

							if (EapTlsResult_Success != iRes)
							{
								iRes = EapTlsResult_FailedInstallingCertificates;
								currentState = ConnectionManagerState_Error_Exit;
								continue;
							}
						}
					}
				}
				else
				{
					iRes = EapTlsResult_FailedInstallingCertificates;
					currentState = ConnectionManagerState_Error_Exit;

					EapTls_Log("Error: null WebAPI response\n");
					continue;
				}

				// We now have the new certs installed, let's attempt to add the EAP-TLS network, or configure the duplicated one if we're coming from that path
				currentState = duplicatingNetwork ? ConnectionManagerState_ConfigureEapTlsNetwork_Dup : ConnectionManagerState_AddEapTlsNetwork;
			}
			break;

			// Optimizing similar state handling
			case ConnectionManagerState_ConfigureEapTlsNetwork: // Configure the EAP_TLS's network security (installing CA/Client certs)
			case ConnectionManagerState_ConfigureEapTlsNetwork_Dup: // Configure the EAP_TLS's <TEMPORARY CLONE> network security (installing CA/Client certs)
			{
				EapTls_Log("EapTls_RunConnectionManager::%s\n", duplicatingNetwork ? "ConnectionManagerState_ConfigureEapTlsNetwork_Dup" : "ConnectionManagerState_ConfigureEapTlsNetwork");

				EapTlsConfig *network = duplicatingNetwork ? &radiusNetwork_dup : &radiusNetwork;

				if (EapTls_ConfigureNetworkSecurity(network->eapTlsNetworkName, network->eapTlsClientIdentity, network->eapTlsRootCertificate.id, network->eapTlsClientCertificate.id) != EapTlsResult_Success)
				{
					EapTls_Log("Cannot configure network security for '%s'\n", network->eapTlsNetworkName);

					// Certificates from the WebAPI are not valid --> return to the App
					iRes = EapTlsResult_FailedConfiguringCertificates;
					currentState = ConnectionManagerState_Error_Exit;
				}
				else
				{
					// Network is configured: let's connect
					currentState = duplicatingNetwork ? ConnectionManagerState_ConnectToEapTlsNetwork_Dup : ConnectionManagerState_ConnectToEapTlsNetwork;			
				}
			}
			break;

			case ConnectionManagerState_ConnectToEapTlsNetwork: // Attempt connecting to the EAP_TLS network
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_ConnectToEapTlsNetwork\n");

				duplicatingNetwork = false; // reset the connection-attempt state
				switch ((iRes = EapTls_ConnectToNetwork(radiusNetwork.eapTlsNetworkName)))
				{
					case EapTlsResult_Connecting:
					{
						// We just stay in this state until something happens, ultimately a timeout
					}
					break;

					case EapTlsResult_Connected:
					{
						currentState = ConnectionManagerState_Connected_Exit;

						EapTls_Log("Successfully connected to EAP-TLS network '%s'\n", radiusNetwork.eapTlsNetworkName);
					}
					break;

					case EapTlsResult_FailedTargetingNetwork:
					case EapTlsResult_FailedScanningNetwork:
					{
						// Timeout forcing a connection to the EAP-TLS network --> let's give back control to the app to decide
						currentState = ConnectionManagerState_Error_Exit;

						EapTls_Log("Failed targeting EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
					}
					break;

					case EapTlsResult_NetworkUnknown:
					{
						// The network is unknown --> this means that we might have a configuration issue (i.e. device relocated somewhere else)
						// Let's contact the Web API for a full re-provisioning						
						requestRootCaCertificate = true;
						requestClientCertificate = true;
						currentState = ConnectionManagerState_RequestCertificates;

						EapTls_Log("Unknown EAP-TLS network '%s' --> re-provisioning the device.\n", radiusNetwork.eapTlsNetworkName);
					}
					break;

					case EapTlsResult_ConnectionError:
					{
						// Generic error connecting to the network --> let's give back control to the app to decide
						currentState = ConnectionManagerState_Error_Exit;

						EapTls_Log("Connection error to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
					}
					break;

					case EapTlsResult_AuthenticationError:
					{
						requestRootCaCertificate = true;
						requestClientCertificate = true;

						// We cannot connect to the network because of authentication failure --> let's request new certificates and test them on a temporary duplicated network
						duplicatingNetwork = true;
						currentState = ConnectionManagerState_RequestCertificates;

						EapTls_Log("Error [%d] authenticating to EAP-TLS network '%s' --> requesting new certificates for a duplicated network configuration.\n", iRes, radiusNetwork.eapTlsNetworkName);
					}
					break;
					case EapTlsResult_AuthenticationError_InvalidRootCaCert:
					{
						requestRootCaCertificate = true;
						requestClientCertificate = false;

						// We cannot connect to the network for authentication failure --> let's request new certificates and test them on a temporary duplicated network
						duplicatingNetwork = true;
						currentState = ConnectionManagerState_RequestCertificates;

						EapTls_Log("Error [%d] authenticating to EAP-TLS network '%s' --> requesting new certificates for a duplicated network configuration.\n", iRes, radiusNetwork.eapTlsNetworkName);
					}
					break;
					case EapTlsResult_AuthenticationError_InvalidClientCert:
					case EapTlsResult_AuthenticationError_InvalidClientIdentity:
					{
						requestRootCaCertificate = false;
						requestClientCertificate = true;

						// We cannot connect to the network for authentication failure --> let's request new certificates and test them on a temporary duplicated network
						duplicatingNetwork = true;
						currentState = ConnectionManagerState_RequestCertificates;

						EapTls_Log("Error [%d] authenticating to EAP-TLS network '%s' --> requesting new certificates for a duplicated network configuration.\n", iRes, radiusNetwork.eapTlsNetworkName);
					}
					break;

					case EapTlsResult_NetworkDisabled:
					{
						// For some reason, the network is disabled despite the target-scan EapTls_ConnectToNetwork. This may be due to other user threads concurrency --> let's give back control to the app to decide
						//iRes = EapTlsResult_NetworkDisabled;
						currentState = ConnectionManagerState_Error_Exit;

						EapTls_Log("Error handling connection to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
					}
					break;

					case EapTlsResult_ConnectionTimeout:
					{
						// Timeout connecting to the network for technical reasons (as no diagnostics are available) --> let's give back control to the app to decide
						//iRes = EapTlsResult_ConnectionTimeout;
						currentState = ConnectionManagerState_Error_Exit;

						EapTls_Log("Timeout connecting to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
					}
					break;

					case EapTlsResult_FailedDiagnosingNetwork:
					{
						// For some reason, the network is disabled despite the target-scan EapTls_ConnectToNetwork. This may be due to other user threads concurrency --> let's give back control to the app to decide
						//iRes = EapTlsResult_FailedDiagnosingNetwork;
						currentState = ConnectionManagerState_Error_Exit;

						EapTls_Log("Error handling connection to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
					}
					break;

					case EapTlsResult_Error:
					{
						// Failed initializing connection handler --> let's give back control to the app to decide
						//iRes = EapTlsResult_Error;
						currentState = ConnectionManagerState_Error_Exit;

						EapTls_Log("Failed initializing connection handler to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
					}
					break;

					default:
					{
						// DEV ERROR: should never get here, block execution to check call-stack.
						currentState = ConnectionManagerState_TBD;
					}
					break;
				}
			}
			break;

			case ConnectionManagerState_ConnectToEapTlsNetwork_Dup: // Attempt connecting to the EAP_TLS's <TEMPORARY CLONE> network
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_ConnectToEapTlsNetwork_Dup\n");
				switch ((iRes = EapTls_ConnectToNetwork(radiusNetwork_dup.eapTlsNetworkName)))
				{
					case EapTlsResult_Connecting:
					{
						// We just stay in this state until something happens, ultimately a timeout
					}
					break;

					case EapTlsResult_Connected:
					{
						// Successfully connected to the new EAP_TLS network --> let's swap it with the original one
						currentState = ConnectionManagerState_SwapEapTlsNetworks;

						EapTls_Log("Successfully connected to the NEW EAP-TLS network '%s'\n", radiusNetwork_dup.eapTlsNetworkName);
					}
					break;

					default:
					{
						// Any other error on this second attempt (on the duplicated network), is a total fail. Therefore the state-machine is reset back to the App's control.
						// Failed connecting to the duplicated network --> let's give back control to the app to decide, with the last error in iRes
						currentState = ConnectionManagerState_Error_Exit;

						EapTls_Log("Failed connecting to NEW EAP-TLS network '%s' --> exiting\n", radiusNetwork_dup.eapTlsNetworkName);
					}
					break;
				}
			}
			break;

			case ConnectionManagerState_SwapEapTlsNetworks: // Swap the original EAP_TLS network with the EAP_TLS's <TEMPORARY CLONE> network
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_SwapEapTlsNetworks\n");

				// Delete the original network configuration
				int networkId = WifiConfig_GetNetworkIdByConfigName(radiusNetwork.eapTlsNetworkName);
				if (-1 == networkId)
				{
					iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
					currentState = ConnectionManagerState_Error_Exit;
					EapTls_Log("Error looking-up network '%s' - Id[%d]: errno=%d (%s) --> exiting\n", radiusNetwork.eapTlsNetworkName, networkId, errno, strerror(errno));
				}
				else
				{
					// Delete the original network configuration
					if (-1 == WifiConfig_ForgetNetworkById(networkId))
					{
						iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
						currentState = ConnectionManagerState_Error_Exit;
						EapTls_Log("Error forgetting network '%s' - Id[%d]: errno=%d (%s) --> exiting\n", radiusNetwork.eapTlsNetworkName, networkId, errno, strerror(errno));
					}
					else
					{
						// Rename the temporary network with the original network configuration name
						networkId = WifiConfig_GetNetworkIdByConfigName(radiusNetwork_dup.eapTlsNetworkName);
						if (-1 == networkId)
						{
							iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
							currentState = ConnectionManagerState_Error_Exit;
							EapTls_Log("Error looking-up network '%s' - Id[%d]: errno=%d (%s) --> exiting\n", radiusNetwork_dup.eapTlsNetworkName, networkId, errno, strerror(errno));
						}
						else
						{
							// Rename the duplicated network to become the first configuration					
							int res = WifiConfig_SetConfigName(networkId, radiusNetwork.eapTlsNetworkName);
							if (-1 == res)
							{
								iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
								currentState = ConnectionManagerState_Error_Exit;
								EapTls_Log("Error renaming network '%s' - Id[%d]: errno=%d (%s) --> exiting\n", radiusNetwork_dup.eapTlsNetworkName, networkId, errno, strerror(errno));
							}
							else
							{
								if (EapTls_PersistNetworkConfig() == EapTlsResult_Success)
								{
									// Rename the NEW root & client certificates
									res = CertStore_MoveCertificate(radiusNetwork_dup.eapTlsRootCertificate.id, radiusNetwork.eapTlsRootCertificate.id);
									if (-1 == res)
									{
										iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
										currentState = ConnectionManagerState_Error_Exit;
										EapTls_Log("Error renaming RootCA certificate '%s': errno=%d (%s) --> exiting\n", radiusNetwork_dup.eapTlsClientCertificate.id, errno, strerror(errno));
									}
									else
									{
										res = CertStore_MoveCertificate(radiusNetwork_dup.eapTlsClientCertificate.id, radiusNetwork.eapTlsClientCertificate.id);
										if (-1 == res)
										{
											iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
											currentState = ConnectionManagerState_Error_Exit;
											EapTls_Log("Error renaming Client certificate '%s': errno=%d (%s) --> exiting\n", radiusNetwork_dup.eapTlsClientCertificate.id, errno, strerror(errno));
										}
										else
										{
											iRes = EapTlsResult_Connected;
											currentState = ConnectionManagerState_Connected_Exit;
											EapTls_Log("Successfully configured new '%s' EAP-TLS network!\n", radiusNetwork.eapTlsNetworkName);
										}
									}
								}
								else
								{
									iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
									currentState = ConnectionManagerState_Error_Exit;
									EapTls_Log("Cannot persist network configuration: errno=%d (%s) --> exiting\n", errno, strerror(errno));
								}
							}
						}
					}
				}
			}
			break;

			case ConnectionManagerState_ConnectToBootstrapNetwork: // Attempt connecting to a bootstrap network, in order to connect to the WebAPI and retrieve new certificates
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_ConnectToBootstrapNetwork\n");

				iRes = EapTls_SetBootstrapNetworkEnabledState(eapTlsConfig, true);
				if (EapTlsResult_Success == iRes)
				{
					if (NetworkInterfaceType_Ethernet == radiusNetwork.bootstrapNetworkInterfaceType)
					{
						// We're on Ethernet: let's directly call the WebAPI
						EapTls_Log("Bootstrap network is on Ethernet: directly calling the WebAPI...\n");
						iRes = EapTlsResult_Connected;
						currentState = ConnectionManagerState_CallMdmWebApi;
					}
					else
					{
						EapTls_Log("Bootstrap network is on Wi-Fi: attempting to connect...\n");
						switch ((iRes = EapTls_ConnectToNetwork(radiusNetwork.bootstrapNetworkName)))
						{
							case EapTlsResult_Connecting:
							{
								// We just stay in this state
							}
							break;

							case EapTlsResult_Connected:
							{
								// Let's call the WebAPI
								currentState = ConnectionManagerState_CallMdmWebApi;
							}
							break;

							default:
							{
								// We cannot connect to the Bootstrap network --> let's give back control to the App to decide what to do next
								iRes = EapTlsResult_FailedConnectingToBootstrapNetwork;
								currentState = ConnectionManagerState_Error_Exit;
							}
							break;
						}
					}
				}
				else
				{
					// We cannot connect enable the Bootstrap network --> let's give back control to the App to decide what to do next
					iRes = EapTlsResult_FailedConnectingToBootstrapNetwork;
					currentState = ConnectionManagerState_Error_Exit;
				}

			}
			break;

			case ConnectionManagerState_RequestCertificates: // Initiate a new Client certificate request
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_RequestCertificates\n");

				currentState = ConnectionManagerState_ConnectToBootstrapNetwork;
			}
			break;

			case ConnectionManagerState_CallMdmWebApi: // Request a new Client certificate to the WebAPI/CMS
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_CallMdmWebApi\n");

				iRes = EapTls_CallMdmWebApi(&radiusNetwork, requestRootCaCertificate, requestClientCertificate, &webApiResponseBlob);
				if (EapTlsResult_Success == iRes)
				{
					// The WebAPI has successfully returned a response --> let's move to parsing
					currentState = ConnectionManagerState_HandleMdmWebApiResponse;
				}
				else
				{
					iRes = EapTlsResult_FailedConnectingToMdmWebApi;
					currentState = ConnectionManagerState_Error_Exit;
					EapTls_Log("Failed calling WebAPI '%s' --> exiting\n", radiusNetwork.mdmWebApiInterfaceUrl);
				}
			}
			break;

			case ConnectionManagerState_HandleMdmWebApiResponse: // Handle the response from the WebAPI/CMS
			{
				EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_HandleMdmWebApiResponse\n");

				// The WebAPI "should" have returned new certificates in the webApiResponseBlock MemoryBlock
				// The response will be a JSON in the following format:
				// {
				//		"timestamp" : "2020-05-22T10:302:25.6828914+00:00", 
				//		"rootCACertficate" : "<certificate in PEM format>", 
				//		"eapTlsNetworkSsid" : "<the SId of the RADIUS network", 
				//		"clientIdentity" : "<the client user identity>"
				//		"clientPublicCertificate" : "<certificate in PEM format>"
				//		"clientPrivateKey" : "<client's private in PEM format>"
				//		"clientPrivateKeyPass" : "<private key password>"
				// }

				free(webApiResponse);
				webApiResponse = malloc(sizeof(WebApiResponse));
				if (NULL != webApiResponse)
				{
					// Parse the WebAPI response and extract the optional RootCA certificate and the Client certificate
					iRes = EapTls_ParseMdmWebApiResponse(&webApiResponseBlob, webApiResponse);
					if (EapTlsResult_Success == iRes)
					{
						// Check if the Web API has returned what we asked for
						if ((requestRootCaCertificate && 0 == *webApiResponse->rootCACertficate) || (requestClientCertificate && 0 == *webApiResponse->clientPublicCertificate))
						{
							iRes = EapTlsResult_FailedReceivingNewCertificates;

							EapTls_Log("Failed receiving the requested certificates from then WebAPI '%s' (RootCA-%s, ClientCert=%s) --> exiting\n",
								(requestRootCaCertificate && 0 == *webApiResponse->rootCACertficate) ? "OK" : "failed",
								(requestClientCertificate && 0 == *webApiResponse->clientPublicCertificate) ? "OK" : "failed",
								radiusNetwork.mdmWebApiInterfaceUrl);
						}
						else
						{
							// The WebAPI has returned new certificate(s) and stored them in the function-global webApiResponse variable				

							// Set and persist the assigned RADIUS network's SSID					
							strncpy(deviceConfiguration.eapTlsNetworkSsid, webApiResponse->eapTlsNetworkSsid, sizeof(deviceConfiguration.eapTlsNetworkSsid) - 1);
							if (EapTlsResult_Success == EapTls_StoreDeviceConfiguration(&deviceConfiguration))
							{
								strncpy(radiusNetwork.eapTlsNetworkSsid, deviceConfiguration.eapTlsNetworkSsid, sizeof(radiusNetwork.eapTlsNetworkSsid) - 1);
							}

							if (requestClientCertificate)
							{
								// The WebAPI has returned a new client certificate, so let's set & persist its related client's identity														
								strncpy(deviceConfiguration.eapTlsClientIdentity, webApiResponse->clientIdentity, sizeof(deviceConfiguration.eapTlsClientIdentity) - 1);
								if (EapTlsResult_Success == EapTls_StoreDeviceConfiguration(&deviceConfiguration))
								{
									strncpy(radiusNetwork.eapTlsClientIdentity, deviceConfiguration.eapTlsClientIdentity, sizeof(radiusNetwork.eapTlsClientIdentity) - 1);
								}
							}

							// Now split paths: differentiate if we're coming from the first (failed) attempt, or if this is just the first EAP_TLS installation
							if (duplicatingNetwork)
							{
								// We first clone the network, then we install the certificates from that state
								currentState = ConnectionManagerState_CloneEapTlsNetwork;
							}
							else
							{
								currentState = ConnectionManagerState_Installcerts;
							}
						}
					}
					else
					{
						iRes = EapTlsResult_FailedParsingMdmWebApiResponse;
						currentState = ConnectionManagerState_Error_Exit;
						EapTls_Log("Failed parsing response from WebAPI '%s' --> exiting\n", radiusNetwork.mdmWebApiInterfaceUrl);
					}
				}
				else
				{
					iRes = EapTlsResult_FailedParsingMdmWebApiResponse;
					currentState = ConnectionManagerState_Error_Exit;
					EapTls_Log("Out of memory parsing response from WebAPI '%s' --> exiting\n", radiusNetwork.mdmWebApiInterfaceUrl);
				}
				free(webApiResponseBlob.data);
				webApiResponseBlob.data = NULL;
			}
			break;

			case ConnectionManagerState_Connected_Exit: // Successfully connected to the EAP-TLS network -> the App can proceed its execution
			{
				EapTls_Log("EapTls_RunConnectionManager::EAP_TLS_CONNECTED_EXIT - result=%d\n", iRes);
				exitStateMachine = true;
			}
			break;

			case ConnectionManagerState_Error_Exit: // FAILED to connect to the EAP-TLS network -> let's return so the App can decide the next steps
			{
				EapTls_Log("EapTls_RunConnectionManager::EAP_TLS_ERROR_EXIT - result=%d\n", iRes);
				exitStateMachine = true;
			}
			break;

			default:
				EapTls_Log("EapTls_RunConnectionManager::UNDEFINED STATE '%d'!\n", currentState);
				currentState = ConnectionManagerState_TBD;
				break;
		}
	}

	if (NULL != webApiResponse)
	{
		free(webApiResponse);
	}

	return iRes;
}