EapTlsResult EapTls_CallWebApi()

in EAP-TLS_Solution/EAP-TLS Client/lib/web_api_client.c [201:382]


EapTlsResult EapTls_CallWebApi(const char *url, const char *queryString, const char *putString, const char *webApiRootCACertRelativePath, MemoryBlock *responseBlock)
{
	EapTlsResult iRes = EapTlsResult_Error;

	if (NULL != url && NULL != webApiRootCACertRelativePath && NULL != responseBlock)
	{
		// https://curl.haxx.se/libcurl/c/https.html
		curl_global_init(CURL_GLOBAL_ALL);
		CURL *curlHandle = curl_easy_init();
		if (curlHandle)
		{
			CURLcode res;
			// Set up for DAA mutual authentication
			// Device: https://learn.microsoft.com/en-us/azure-sphere/app-development/curl
			// WebAPI: https://learn.microsoft.com/en-us/azure/app-service/app-service-web-configure-tls-mutual-auth#special-considerations-for-certificate-validation

			// Set up the WebAPI's URL with the proper GET query
			char call_url[MAX_URL_LEN + 1];
			memset(call_url, 0, sizeof(call_url));
			if (NULL != queryString)
			{
				snprintf(call_url, sizeof(call_url), "%s?%s", url, queryString);
			}
			else
			{
				strncpy(call_url, url, sizeof(call_url) - 1);
			}

			// Activate verbose logging
			if ((res = curl_easy_setopt(curlHandle, CURLOPT_VERBOSE, 1L)) != CURLE_OK)
			{
				LogCurlError("FAILED curl_easy_setopt CURLOPT_VERBOSE", res);
			}
			if ((res = curl_easy_setopt(curlHandle, CURLOPT_URL, call_url)) != CURLE_OK)
			{
				LogCurlError(" FAILED curl_easy_setopt CURLOPT_URL", res);
			}
			else
			{
				// Set up the PUT fields, if any
				if (NULL != putString)
				{
					if ((res = curl_easy_setopt(curlHandle, CURLOPT_POST, 1L)) != CURLE_OK)
					{
						LogCurlError(" FAILED curl_easy_setopt CURLOPT_POST", res);
					}
					else
					{
						struct curl_slist *hs = NULL;
						hs = curl_slist_append(hs, "Content-Type: application/json");
						if ((res = curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, hs)) != CURLE_OK)
						{
							LogCurlError(" FAILED curl_easy_setopt CURLOPT_HTTPHEADER", res);
						}
						else
						{
							char hdrTemp[MAX_URL_LEN + 1];
							snprintf(hdrTemp, sizeof(hdrTemp), "Content-Length: %zd", strlen(putString));
							hs = curl_slist_append(hs, hdrTemp);
							if ((res = curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, hs)) != CURLE_OK)
							{
								LogCurlError(" FAILED curl_easy_setopt CURLOPT_HTTPHEADER", res);
							}
							else
							{
								if ((res = curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, putString)) != CURLE_OK)
								{
									LogCurlError(" FAILED curl_easy_setopt CURLOPT_POSTFIELDS", res);
								}
							}
						}
					}
				}
				//if ((res = curl_easy_setopt(curlHandle, CURLOPT_SSL_VERIFYHOST, 0)) != CURLE_OK)
				//{
				//	LogCurlError("curl_easy_setopt CURLOPT_SSL_VERIFYHOST", res);
				//}
				if ((res = curl_easy_setopt(curlHandle, CURLOPT_SSL_VERIFYPEER, 0)) != CURLE_OK)
				{
					LogCurlError("FAILED curl_easy_setopt CURLOPT_SSL_VERIFYPEER", res);
				}
				else
				{
					// The simplest way to perform device authentication is to configure DeviceAuth_CurlSslFunc as the callback function for curl SSL authentication
					if ((res = curl_easy_setopt(curlHandle, CURLOPT_SSL_CTX_FUNCTION, EapTls_DeviceAuth_CurlSslFunc)) != CURLE_OK)
					{
						LogCurlError("curl_easy_setopt CURLOPT_SSL_CTX_FUNCTION", res);
					}
					else
					{
						// libcurl on Azure Sphere supports TLS 1.2 and has deprecated TLS 1.0 and TLS 1.1 in alignment with the broader Microsoft TLS security strategy
						if ((res = curl_easy_setopt(curlHandle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2)) != CURLE_OK)
						{
							LogCurlError("FAILED curl_easy_setopt CURLOPT_SSLVERSION", res);
						}
						else
						{
							// Get the full path to the certificate file used to authenticate the WebAPI's server identity
							// #NOTE: currently APIs do not support passing libcurl a certificate Id
							char *certificatePath = Storage_GetAbsolutePathInImagePackage(webApiRootCACertRelativePath);
							if (certificatePath == NULL)
							{
								Log_Debug("The certificate path could not be resolved: errno=%d (%s)\n", errno, strerror(errno));
							}
							else
							{
								// Set the path for the certificate file that cURL uses to validate the server certificate
								if ((res = curl_easy_setopt(curlHandle, CURLOPT_CAINFO, certificatePath)) != CURLE_OK)
								{
									LogCurlError("curl_easy_setopt CURLOPT_CAINFO", res);
								}
								else
								{
									// Let cURL follow any HTTP 3xx redirects. Important: any redirection to different domain names
									// requires that domain name to be added to app_manifest.json
									if ((res = curl_easy_setopt(curlHandle, CURLOPT_FOLLOWLOCATION, 1L)) != CURLE_OK)
									{
										LogCurlError("FAILED curl_easy_setopt CURLOPT_FOLLOWLOCATION", res);
									}
									else
									{
										// Specify a user agent
										if ((res = curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0")) != CURLE_OK)
										{
											LogCurlError("FAILED curl_easy_setopt CURLOPT_USERAGENT", res);
										}
										else
										{
											// Set the custom parameter of the callback to the memory block
											if ((res = curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *)responseBlock)) != CURLE_OK)
											{
												LogCurlError("FAILED curl_easy_setopt CURLOPT_WRITEDATA", res);
											}
											else
											{
												// Set up callback for cURL to use when downloading data
												if ((res = curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, EapTls_StoreDownloadedDataCallback)) != CURLE_OK)
												{
													LogCurlError("FAILED curl_easy_setopt CURLOPT_FOLLOWLOCATION", res);
												}
												else
												{
													Log_Debug("Connecting to %s...\n", call_url);
													if ((res = curl_easy_perform(curlHandle)) == CURLE_OK)
													{
														// #NOTE: SSL renegotiation is not currently supported by Azure Sphere
														Log_Debug("\n -===- Downloaded content (%zu bytes): -===-\n", responseBlock->size);
														Log_Debug("%s\n", responseBlock->data);
														iRes = EapTlsResult_Success;
													}
													else
													{
														LogCurlError("FAILED curl_easy_perform", res);
														iRes = EapTlsResult_FailedConnectingToMdmWebApi;
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}

			curl_easy_cleanup(curlHandle);
		}
		else
		{
			Log_Debug("FAILED initializing cURL!");
			iRes = EapTlsResult_Error;
		}
	}
	else
	{
		Log_Debug("ERROR: bad parameters!");
		iRes = EapTlsResult_BadParameters;
	}

	return iRes;
}