18 #include <aws/core/Aws.h> 19 #include <aws/core/utils/Array.h> 20 #include <aws/core/utils/DateTime.h> 21 #include <aws/core/utils/json/JsonSerializer.h> 22 #include <aws/core/utils/logging/LogMacros.h> 23 #include <curl/curl.h> 29 #define FIELD_CREDENTIALS "credentials" 30 #define FIELD_EXPIRATION "expiration" 32 #define FIELD_ACCESS_KEY "accessKeyId" 34 #define FIELD_SECRET_KEY "secretAccessKey" 36 #define FIELD_SESSION_TOKEN "sessionToken" 40 #define HEADER_THING_NAME "x-amzn-iot-thingname" 42 #ifndef MAX_IOT_CREDENTIAL_BYTES 43 #define MAX_IOT_CREDENTIAL_BYTES 65535 49 static const char *
AWS_LOG_TAG =
"ServiceCredentialsProviderChain";
64 static bool SetCurlOpt(CURL * curl, CURLoption opt, T lvalue);
65 static bool AppendHeader(
struct curl_slist ** headers,
const char * name,
const char * value);
70 static bool GetConfigValue(std::map<std::string, std::string> & data,
const char * name, T & result,
71 bool optional =
false);
82 static bool SetCurlOpt(CURL * curl, CURLoption opt, T lvalue)
84 CURLcode res = curl_easy_setopt(curl, opt, lvalue);
85 if (res != CURLE_OK) {
86 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Error setting curl option: %s", curl_easy_strerror(res));
104 static bool AppendHeader(
struct curl_slist ** headers,
const char * name,
const char * value)
106 Aws::StringStream stream;
107 stream << name <<
": " << value;
109 struct curl_slist * next = curl_slist_append(*headers, stream.str().c_str());
110 if (next ==
nullptr) {
111 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Error setting header[%s]: %s", name, value);
126 return config.
cafile.length() > 0 && config.
certfile.length() > 0 &&
127 config.
keyfile.length() > 0 && config.
host.length() > 0 && config.
role.length() > 0 &&
139 result = Aws::String(value.c_str(), value.size());
153 int32_t tmp = Aws::Utils::StringUtils::ConvertToInt32(value.c_str());
170 template <
typename T>
171 static bool GetConfigValue(std::map<std::string, std::string> & data,
const char * name, T & result,
174 auto it = data.find(name);
175 if (it != data.end()) {
182 AWS_LOG_DEBUG(
AWS_LOG_TAG,
"IoT provider: Missing %s configuration value", name);
197 const std::shared_ptr<Aws::Client::ParameterReaderInterface>& parameters)
200 Aws::String cafile, certfile, keyfile, host, role, name;
204 std::map<std::string, std::string> data;
216 AWS_LOG_INFO(
AWS_LOG_TAG,
"Could not find config value %s, using default %d",
221 AWS_LOG_INFO(
AWS_LOG_TAG,
"Could not find config value %s, using default %d",
237 "IoT provider config: ca=%s,cert=%s,key=%s,ep=%s,role=%s,thing_name=%s," 238 "connect_timeout=%d,total_timeout=%d",
246 "Missing or incomplete 'iot' parameters, skipping IoT credential provider");
281 static size_t WriteData(
char * ptr,
size_t ,
size_t nmemb,
void * userdata)
284 size_t current = ctx->
stream_.tellp();
289 "IoT response was too large, current:%d bytes, read:%d bytes, max:%d bytes",
294 ctx->stream_.write(ptr, nmemb);
304 return Aws::Utils::Json::JsonValue(
stream_.str());
318 : cached_(
"",
""), config_(config), expiry_(0.0)
329 AWS_LOG_DEBUG(
AWS_LOG_TAG,
"Timer has expired, refreshing AWS IoT Role credentials");
358 if (!value.WasParseSuccessful()) {
359 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Unable to parse JSON response from AWS IoT credential provider");
363 auto value_view = value.View();
365 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Unable to find %s field in AWS IoT credential provider response",
372 if (!creds.IsObject()) {
373 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Expected object for %s in AWS IoT credential provider response",
379 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Unable to find %s field in AWS IoT credential provider response",
399 AWS_LOG_INFO(
AWS_LOG_TAG,
"Found valid credentials response from IoT");
415 CURL * curl =
nullptr;
416 struct curl_slist * headers =
nullptr;
418 AWS_LOG_INFO(
AWS_LOG_TAG,
"Retrieving IOT credentials!");
426 curl = curl_easy_init();
427 if (curl ==
nullptr) {
428 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Could not initialize curl!");
430 Aws::StringStream url_stream;
434 if (!
SetCurlOpt(curl, CURLOPT_SSL_VERIFYPEER, 1L)) {
goto cleanup_curl; }
435 if (!
SetCurlOpt(curl, CURLOPT_SSL_VERIFYHOST, 2L)) {
goto cleanup_curl; }
436 if (!
SetCurlOpt(curl, CURLOPT_HTTPGET, 1L)) {
goto cleanup_curl; }
437 if (!
SetCurlOpt(curl, CURLOPT_HEADER, 0L)) {
goto cleanup_curl; }
441 if (!
SetCurlOpt(curl, CURLOPT_URL, url_stream.str().c_str())) {
goto cleanup_curl; }
446 if (!
SetCurlOpt(curl, CURLOPT_SSLCERTTYPE,
"PEM")) {
goto cleanup_curl; }
449 if (!
SetCurlOpt(curl, CURLOPT_SSLKEYTYPE,
"PEM")) {
goto cleanup_curl; }
455 if (!
SetCurlOpt(curl, CURLOPT_WRITEDATA, &ctx)) {
goto cleanup_curl; }
458 if (!
AppendHeader(&headers,
"Accept",
"application/json")) {
goto cleanup_curl; }
462 if (!
SetCurlOpt(curl, CURLOPT_HTTPHEADER, headers)) {
goto cleanup_curl; }
465 res = curl_easy_perform(curl);
466 if (res != CURLE_OK) {
467 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Error when curling endpoint: %s", curl_easy_strerror(res));
480 Aws::Utils::DateTime expiration(expires_str, Aws::Utils::DateFormat::ISO_8601);
481 if (!expiration.WasParseSuccessful()) {
482 AWS_LOG_ERROR(
AWS_LOG_TAG,
"Unable to parse expiration: %s", expires_str.c_str());
485 AWS_LOG_INFO(
AWS_LOG_TAG,
"Retrieved AWS creds from IoT, next expiration %s",
486 expires_str.c_str());
492 expiry_.store(expiration.SecondsWithMSPrecision());
496 if (headers !=
nullptr) {
497 curl_slist_free_all(headers);
501 if (curl !=
nullptr) {
502 curl_easy_cleanup(curl);
517 AWS_LOG_INFO(
AWS_LOG_TAG,
"Found valid IoT auth config, adding IotRoleCredentialsProvider");
518 auto provider = Aws::MakeShared<IotRoleCredentialsProvider>(__func__, config.
iot);
519 AddProvider(provider);
521 AWS_LOG_INFO(
AWS_LOG_TAG,
"No valid IoT auth config, skipping IotRoleCredentialsProvider");
static const char CFG_THING_NAME[]
void Refresh()
Refreshes the cached AWS credentials.
static const char * AWS_LOG_TAG
Logging tag used for all messages emitting from this module.
Aws::String host
Host name of the iot:CredentialProvider endpoint.
static bool SetCurlOpt(CURL *curl, CURLoption opt, T lvalue)
Helper to set a libcurl option or log an error if a problem occurred.
static bool ParseConfigValue(std::string &value, int32_t &result)
Helper to parse a configuration value into an Aws::String variable This function assumes that the val...
AWSCredentials GetAWSCredentials() override
static const char CFG_TOTAL_TIMEOUT_MS[]
std::mutex creds_mutex_
Mutex to ensure only a single request is outstanding at any given time.
IotRoleConfig iot
IoT-specific configuration.
static const char CFG_CONNECT_TIMEOUT_MS[]
#define HEADER_THING_NAME
Header name to append to the request containing the thing name.
static const char CFG_CAFILE[]
static const long DEFAULT_AUTH_TOTAL_TIMEOUT_MS
Default number of milliseconds to wait before timing out when retrieving credentials from IoT...
void SetCredentials(AWSCredentials &creds_obj)
Sets the cached credentials.
Encapsulates the response from a curl request Curl uses a callback when performing a request for chun...
static const char CFG_CERTFILE[]
~IotRoleCredentialsProvider() override
static bool IsIotConfigValid(const IotRoleConfig &config)
Validates an instance of an IotRoleConfig struct.
static size_t WriteData(char *ptr, size_t, size_t nmemb, void *userdata)
Curl callback for CURLOPT_WRITEFUNCTION Recieves callbacks from the curl library any time data is rec...
#define MAX_IOT_CREDENTIAL_BYTES
long connect_timeout_ms
Number of ms to wait before timing out when connecting to the endpoint.
static const char CFG_ROLE[]
IotRoleCredentialsProvider(const IotRoleConfig &config)
#define FIELD_SESSION_TOKEN
Name of the field containing the session token in the json response from IoT.
static const char CFG_KEYFILE[]
Aws::String cafile
Path to the Root CA for the endpoint.
IotRoleConfig config_
Configuration for connecting to IoT.
Auth configuration needed to retrieve AWS credentials via the IoT service.
Aws::String name
Thing name for the device.
#define FIELD_CREDENTIALS
Name of the credentials object in the json response from IoT.
#define FIELD_ACCESS_KEY
Name of the field containing the access key id in the json response from IoT.
static const long DEFAULT_AUTH_CONNECT_TIMEOUT_MS
Default number of milliseconds to wait before timing out when connecting to retrieve credentials from...
Aws::String certfile
Path to the certificate which identifies the device.
#define FIELD_EXPIRATION
Name of the field containing the expiration date in the json response from IoT.
bool IsTimeExpired()
Returns true if the credentials have expired.
Aws::String role
Name of the AWS IoT Role Alias for the device.
#define FIELD_SECRET_KEY
Name of the field containing the secret key in the json response from IoT.
Contains common Error handling functionality for AWS ROS libraries.
static bool AppendHeader(struct curl_slist **headers, const char *name, const char *value)
Appends a name/value pair to a list of curl headers The libcurl API potentially returns new list poin...
static bool ParseConfigVale(std::string &value, Aws::String &result)
static const char CFG_ENDPOINT[]
std::atomic< double > expiry_
Future epoch when the cached credentials will expire.
Aws::Utils::Json::JsonValue GetValue()
Create a Json::JsonValue object from the current value of the buffer.
bool ValidateResponse(Aws::Utils::Json::JsonValue &value)
Validates the json response from the AWS IoT service.
Aws::Auth::AWSCredentials cached_
Current cached credentials.
ServiceCredentialsProviderChain()
long total_timeout_ms
Total number of ms to wait for the entire connect/request/response transaction.
bool GetServiceAuthConfig(ServiceAuthConfig &config, const std::shared_ptr< Aws::Client::ParameterReaderInterface > ¶meters)
Retrieves service authorization data from a ParameterReaderInterface and populates the ServiceAuthCon...
Aws::String keyfile
Path to the related private key for the certificate.
~RequestContext()=default
static bool GetConfigValue(std::map< std::string, std::string > &data, const char *name, T &result, bool optional=false)
Helper to retrieve a specific config value from a map Simple helper to try to retrieve data from a ma...
Aws::StringStream stream_
Current data that has be received so far.
static const double EXPIRATION_GRACE_BUFFER
Go ahead and try to refresh credentials 30s before expiration.
Auth configuration for ROS AWS service integration.