1/*
2 * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16
17#include <aws/core/auth/AWSCredentialsProvider.h>
18
19#include <aws/core/config/AWSProfileConfigLoader.h>
20#include <aws/core/platform/Environment.h>
21#include <aws/core/platform/FileSystem.h>
22#include <aws/core/platform/OSVersionInfo.h>
23#include <aws/core/utils/logging/LogMacros.h>
24#include <aws/core/utils/StringUtils.h>
25#include <aws/core/utils/json/JsonSerializer.h>
26#include <aws/core/utils/FileSystemUtils.h>
27#include <aws/core/client/AWSError.h>
28#include <aws/core/utils/StringUtils.h>
29#include <aws/core/utils/xml/XmlSerializer.h>
30#include <cstdlib>
31#include <fstream>
32#include <string.h>
33#include <climits>
34
35
36using namespace Aws::Utils;
37using namespace Aws::Utils::Logging;
38using namespace Aws::Auth;
39using namespace Aws::Internal;
40using namespace Aws::FileSystem;
41using namespace Aws::Utils::Xml;
42using namespace Aws::Client;
43using Aws::Utils::Threading::ReaderLockGuard;
44using Aws::Utils::Threading::WriterLockGuard;
45
46static const char ACCESS_KEY_ENV_VAR[] = "AWS_ACCESS_KEY_ID";
47static const char SECRET_KEY_ENV_VAR[] = "AWS_SECRET_ACCESS_KEY";
48static const char SESSION_TOKEN_ENV_VAR[] = "AWS_SESSION_TOKEN";
49static const char DEFAULT_PROFILE[] = "default";
50static const char AWS_PROFILE_ENV_VAR[] = "AWS_PROFILE";
51static const char AWS_PROFILE_DEFAULT_ENV_VAR[] = "AWS_DEFAULT_PROFILE";
52
53static const char AWS_CREDENTIALS_FILE[] = "AWS_SHARED_CREDENTIALS_FILE";
54extern const char AWS_CONFIG_FILE[] = "AWS_CONFIG_FILE";
55
56extern const char PROFILE_DIRECTORY[] = ".aws";
57static const char DEFAULT_CREDENTIALS_FILE[] = "credentials";
58extern const char DEFAULT_CONFIG_FILE[] = "config";
59
60
61static const int EXPIRATION_GRACE_PERIOD = 5 * 1000;
62
63void AWSCredentialsProvider::Reload()
64{
65 m_lastLoadedMs = DateTime::Now().Millis();
66}
67
68bool AWSCredentialsProvider::IsTimeToRefresh(long reloadFrequency)
69{
70 if (DateTime::Now().Millis() - m_lastLoadedMs > reloadFrequency)
71 {
72 return true;
73 }
74 return false;
75}
76
77
78static const char* ENVIRONMENT_LOG_TAG = "EnvironmentAWSCredentialsProvider";
79
80
81AWSCredentials EnvironmentAWSCredentialsProvider::GetAWSCredentials()
82{
83 auto accessKey = Aws::Environment::GetEnv(ACCESS_KEY_ENV_VAR);
84 AWSCredentials credentials;
85
86 if (!accessKey.empty())
87 {
88 credentials.SetAWSAccessKeyId(accessKey);
89
90 AWS_LOGSTREAM_DEBUG(ENVIRONMENT_LOG_TAG, "Found credential in environment with access key id " << accessKey);
91 auto secretKey = Aws::Environment::GetEnv(SECRET_KEY_ENV_VAR);
92
93 if (!secretKey.empty())
94 {
95 credentials.SetAWSSecretKey(secretKey);
96 AWS_LOGSTREAM_INFO(ENVIRONMENT_LOG_TAG, "Found secret key");
97 }
98
99 auto sessionToken = Aws::Environment::GetEnv(SESSION_TOKEN_ENV_VAR);
100
101 if(!sessionToken.empty())
102 {
103 credentials.SetSessionToken(sessionToken);
104 AWS_LOGSTREAM_INFO(ENVIRONMENT_LOG_TAG, "Found sessionToken");
105 }
106 }
107
108 return credentials;
109}
110
111Aws::String Aws::Auth::GetConfigProfileFilename()
112{
113 auto configFileNameFromVar = Aws::Environment::GetEnv(AWS_CONFIG_FILE);
114 if (!configFileNameFromVar.empty())
115 {
116 return configFileNameFromVar;
117 }
118 else
119 {
120 return Aws::FileSystem::GetHomeDirectory() + PROFILE_DIRECTORY + PATH_DELIM + DEFAULT_CONFIG_FILE;
121 }
122}
123
124Aws::String Aws::Auth::GetConfigProfileName()
125{
126 auto profileFromVar = Aws::Environment::GetEnv(AWS_PROFILE_DEFAULT_ENV_VAR);
127 if (profileFromVar.empty())
128 {
129 profileFromVar = Aws::Environment::GetEnv(AWS_PROFILE_ENV_VAR);
130 }
131
132 if (profileFromVar.empty())
133 {
134 return Aws::String(DEFAULT_PROFILE);
135 }
136 else
137 {
138 return profileFromVar;
139 }
140}
141
142static const char* PROFILE_LOG_TAG = "ProfileConfigFileAWSCredentialsProvider";
143
144Aws::String ProfileConfigFileAWSCredentialsProvider::GetCredentialsProfileFilename()
145{
146 auto credentialsFileNameFromVar = Aws::Environment::GetEnv(AWS_CREDENTIALS_FILE);
147
148 if (credentialsFileNameFromVar.empty())
149 {
150 return Aws::FileSystem::GetHomeDirectory() + PROFILE_DIRECTORY + PATH_DELIM + DEFAULT_CREDENTIALS_FILE;
151 }
152 else
153 {
154 return credentialsFileNameFromVar;
155 }
156}
157
158Aws::String ProfileConfigFileAWSCredentialsProvider::GetProfileDirectory()
159{
160 Aws::String credentialsFileName = GetCredentialsProfileFilename();
161 auto lastSeparator = credentialsFileName.find_last_of(PATH_DELIM);
162 if (lastSeparator != std::string::npos)
163 {
164 return credentialsFileName.substr(0, lastSeparator);
165 }
166 else
167 {
168 return {};
169 }
170}
171
172ProfileConfigFileAWSCredentialsProvider::ProfileConfigFileAWSCredentialsProvider(long refreshRateMs) :
173 m_profileToUse(Aws::Auth::GetConfigProfileName()),
174 m_credentialsFileLoader(GetCredentialsProfileFilename()),
175 m_loadFrequencyMs(refreshRateMs)
176{
177 AWS_LOGSTREAM_INFO(PROFILE_LOG_TAG, "Setting provider to read credentials from " << GetCredentialsProfileFilename() << " for credentials file"
178 << " and " << GetConfigProfileFilename() << " for the config file "
179 << ", for use with profile " << m_profileToUse);
180}
181
182ProfileConfigFileAWSCredentialsProvider::ProfileConfigFileAWSCredentialsProvider(const char* profile, long refreshRateMs) :
183 m_profileToUse(profile),
184 m_credentialsFileLoader(GetCredentialsProfileFilename()),
185 m_loadFrequencyMs(refreshRateMs)
186{
187 AWS_LOGSTREAM_INFO(PROFILE_LOG_TAG, "Setting provider to read credentials from " << GetCredentialsProfileFilename() << " for credentials file"
188 << " and " << GetConfigProfileFilename() << " for the config file "
189 << ", for use with profile " << m_profileToUse);
190}
191
192AWSCredentials ProfileConfigFileAWSCredentialsProvider::GetAWSCredentials()
193{
194 RefreshIfExpired();
195 ReaderLockGuard guard(m_reloadLock);
196 auto credsFileProfileIter = m_credentialsFileLoader.GetProfiles().find(m_profileToUse);
197
198 if(credsFileProfileIter != m_credentialsFileLoader.GetProfiles().end())
199 {
200 return credsFileProfileIter->second.GetCredentials();
201 }
202
203 return AWSCredentials();
204}
205
206
207void ProfileConfigFileAWSCredentialsProvider::Reload()
208{
209 m_credentialsFileLoader.Load();
210 AWSCredentialsProvider::Reload();
211}
212
213void ProfileConfigFileAWSCredentialsProvider::RefreshIfExpired()
214{
215 ReaderLockGuard guard(m_reloadLock);
216 if (!IsTimeToRefresh(m_loadFrequencyMs))
217 {
218 return;
219 }
220
221 guard.UpgradeToWriterLock();
222 if (!IsTimeToRefresh(m_loadFrequencyMs)) // double-checked lock to avoid refreshing twice
223 {
224 return;
225 }
226
227 Reload();
228}
229
230static const char* INSTANCE_LOG_TAG = "InstanceProfileCredentialsProvider";
231
232InstanceProfileCredentialsProvider::InstanceProfileCredentialsProvider(long refreshRateMs) :
233 m_ec2MetadataConfigLoader(Aws::MakeShared<Aws::Config::EC2InstanceProfileConfigLoader>(INSTANCE_LOG_TAG)),
234 m_loadFrequencyMs(refreshRateMs)
235{
236 AWS_LOGSTREAM_INFO(INSTANCE_LOG_TAG, "Creating Instance with default EC2MetadataClient and refresh rate " << refreshRateMs);
237}
238
239
240InstanceProfileCredentialsProvider::InstanceProfileCredentialsProvider(const std::shared_ptr<Aws::Config::EC2InstanceProfileConfigLoader>& loader, long refreshRateMs) :
241 m_ec2MetadataConfigLoader(loader),
242 m_loadFrequencyMs(refreshRateMs)
243{
244 AWS_LOGSTREAM_INFO(INSTANCE_LOG_TAG, "Creating Instance with injected EC2MetadataClient and refresh rate " << refreshRateMs);
245}
246
247
248AWSCredentials InstanceProfileCredentialsProvider::GetAWSCredentials()
249{
250 RefreshIfExpired();
251 ReaderLockGuard guard(m_reloadLock);
252 auto profileIter = m_ec2MetadataConfigLoader->GetProfiles().find(Aws::Config::INSTANCE_PROFILE_KEY);
253
254 if(profileIter != m_ec2MetadataConfigLoader->GetProfiles().end())
255 {
256 return profileIter->second.GetCredentials();
257 }
258
259 return AWSCredentials();
260}
261
262void InstanceProfileCredentialsProvider::Reload()
263{
264 AWS_LOGSTREAM_INFO(INSTANCE_LOG_TAG, "Credentials have expired attempting to repull from EC2 Metadata Service.");
265 m_ec2MetadataConfigLoader->Load();
266 AWSCredentialsProvider::Reload();
267}
268
269void InstanceProfileCredentialsProvider::RefreshIfExpired()
270{
271 AWS_LOGSTREAM_DEBUG(INSTANCE_LOG_TAG, "Checking if latest credential pull has expired.");
272 ReaderLockGuard guard(m_reloadLock);
273 if (!IsTimeToRefresh(m_loadFrequencyMs))
274 {
275 return;
276 }
277
278 guard.UpgradeToWriterLock();
279 if (!IsTimeToRefresh(m_loadFrequencyMs)) // double-checked lock to avoid refreshing twice
280 {
281 return;
282 }
283 Reload();
284}
285
286static const char TASK_ROLE_LOG_TAG[] = "TaskRoleCredentialsProvider";
287
288TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(const char* URI, long refreshRateMs) :
289 m_ecsCredentialsClient(Aws::MakeShared<Aws::Internal::ECSCredentialsClient>(TASK_ROLE_LOG_TAG, URI)),
290 m_loadFrequencyMs(refreshRateMs)
291{
292 AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs);
293}
294
295TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(const char* endpoint, const char* token, long refreshRateMs) :
296 m_ecsCredentialsClient(Aws::MakeShared<Aws::Internal::ECSCredentialsClient>(TASK_ROLE_LOG_TAG, ""/*resourcePath*/, endpoint, token)),
297 m_loadFrequencyMs(refreshRateMs)
298{
299 AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs);
300}
301
302TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(
303 const std::shared_ptr<Aws::Internal::ECSCredentialsClient>& client, long refreshRateMs) :
304 m_ecsCredentialsClient(client),
305 m_loadFrequencyMs(refreshRateMs)
306{
307 AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Creating TaskRole with default ECSCredentialsClient and refresh rate " << refreshRateMs);
308}
309
310AWSCredentials TaskRoleCredentialsProvider::GetAWSCredentials()
311{
312 RefreshIfExpired();
313 ReaderLockGuard guard(m_reloadLock);
314 return m_credentials;
315}
316
317bool TaskRoleCredentialsProvider::ExpiresSoon() const
318{
319 return ((m_credentials.GetExpiration() - Aws::Utils::DateTime::Now()).count() < EXPIRATION_GRACE_PERIOD);
320}
321
322void TaskRoleCredentialsProvider::Reload()
323{
324 AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Credentials have expired or will expire, attempting to repull from ECS IAM Service.");
325
326 auto credentialsStr = m_ecsCredentialsClient->GetECSCredentials();
327 if (credentialsStr.empty()) return;
328
329 Json::JsonValue credentialsDoc(credentialsStr);
330 if (!credentialsDoc.WasParseSuccessful())
331 {
332 AWS_LOGSTREAM_ERROR(TASK_ROLE_LOG_TAG, "Failed to parse output from ECSCredentialService.");
333 return;
334 }
335
336 Aws::String accessKey, secretKey, token;
337 Utils::Json::JsonView credentialsView(credentialsDoc);
338 accessKey = credentialsView.GetString("AccessKeyId");
339 secretKey = credentialsView.GetString("SecretAccessKey");
340 token = credentialsView.GetString("Token");
341 AWS_LOGSTREAM_DEBUG(TASK_ROLE_LOG_TAG, "Successfully pulled credentials from metadata service with access key " << accessKey);
342
343 m_credentials.SetAWSAccessKeyId(accessKey);
344 m_credentials.SetAWSSecretKey(secretKey);
345 m_credentials.SetSessionToken(token);
346 m_credentials.SetExpiration(Aws::Utils::DateTime(credentialsView.GetString("Expiration"), DateFormat::ISO_8601));
347 AWSCredentialsProvider::Reload();
348}
349
350void TaskRoleCredentialsProvider::RefreshIfExpired()
351{
352 AWS_LOGSTREAM_DEBUG(TASK_ROLE_LOG_TAG, "Checking if latest credential pull has expired.");
353 ReaderLockGuard guard(m_reloadLock);
354 if (!m_credentials.IsEmpty() && !IsTimeToRefresh(m_loadFrequencyMs) && !ExpiresSoon())
355 {
356 return;
357 }
358
359 guard.UpgradeToWriterLock();
360
361 if (!m_credentials.IsEmpty() && !IsTimeToRefresh(m_loadFrequencyMs) && !ExpiresSoon())
362 {
363 return;
364 }
365
366 Reload();
367}
368
369static const char PROCESS_LOG_TAG[] = "ProcessCredentialsProvider";
370ProcessCredentialsProvider::ProcessCredentialsProvider() :
371 m_profileToUse(Aws::Auth::GetConfigProfileName())
372{
373 AWS_LOGSTREAM_INFO(PROCESS_LOG_TAG, "Setting process credentials provider to read config from " << m_profileToUse);
374}
375
376ProcessCredentialsProvider::ProcessCredentialsProvider(const Aws::String& profile) :
377 m_profileToUse(profile)
378{
379 AWS_LOGSTREAM_INFO(PROCESS_LOG_TAG, "Setting process credentials provider to read config from " << m_profileToUse);
380}
381
382AWSCredentials ProcessCredentialsProvider::GetAWSCredentials()
383{
384 RefreshIfExpired();
385 ReaderLockGuard guard(m_reloadLock);
386 return m_credentials;
387}
388
389
390void ProcessCredentialsProvider::Reload()
391{
392 auto profile = Aws::Config::GetCachedConfigProfile(m_profileToUse);
393 const Aws::String &command = profile.GetCredentialProcess();
394 if (command.empty())
395 {
396 AWS_LOGSTREAM_ERROR(PROCESS_LOG_TAG, "Failed to find credential process's profile: " << m_profileToUse);
397 return;
398 }
399 m_credentials = GetCredentialsFromProcess(command);
400}
401
402void ProcessCredentialsProvider::RefreshIfExpired()
403{
404 ReaderLockGuard guard(m_reloadLock);
405 if (!m_credentials.IsExpiredOrEmpty())
406 {
407 return;
408 }
409
410 guard.UpgradeToWriterLock();
411 if (!m_credentials.IsExpiredOrEmpty()) // double-checked lock to avoid refreshing twice
412 {
413 return;
414 }
415
416 Reload();
417}
418
419AWSCredentials Aws::Auth::GetCredentialsFromProcess(const Aws::String& process)
420{
421 Aws::String command = process;
422 command.append(" 2>&1"); // redirect stderr to stdout
423 Aws::String result = Aws::Utils::StringUtils::Trim(Aws::OSVersionInfo::GetSysCommandOutput(command.c_str()).c_str());
424 Json::JsonValue credentialsDoc(result);
425 if (!credentialsDoc.WasParseSuccessful())
426 {
427 AWS_LOGSTREAM_ERROR(PROFILE_LOG_TAG, "Failed to load credential from running: " << command << " Error: " << result);
428 return {};
429 }
430
431 Aws::Utils::Json::JsonView credentialsView(credentialsDoc);
432 if (!credentialsView.KeyExists("Version") || credentialsView.GetInteger("Version") != 1)
433 {
434 AWS_LOGSTREAM_ERROR(PROFILE_LOG_TAG, "Encountered an unsupported process credentials payload version:" << credentialsView.GetInteger("Version"));
435 return {};
436 }
437
438 AWSCredentials credentials;
439 Aws::String accessKey, secretKey, token, expire;
440 if (credentialsView.KeyExists("AccessKeyId"))
441 {
442 credentials.SetAWSAccessKeyId(credentialsView.GetString("AccessKeyId"));
443 }
444
445 if (credentialsView.KeyExists("SecretAccessKey"))
446 {
447 credentials.SetAWSSecretKey(credentialsView.GetString("SecretAccessKey"));
448 }
449
450 if (credentialsView.KeyExists("SessionToken"))
451 {
452 credentials.SetSessionToken(credentialsView.GetString("SessionToken"));
453 }
454
455 if (credentialsView.KeyExists("Expiration"))
456 {
457 const auto expiration = Aws::Utils::DateTime(credentialsView.GetString("Expiration"), DateFormat::ISO_8601);
458 if (expiration.WasParseSuccessful())
459 {
460 credentials.SetExpiration(expiration);
461 }
462 else
463 {
464 AWS_LOGSTREAM_ERROR(PROFILE_LOG_TAG, "Failed to parse credential's expiration value as an ISO 8601 Date. Credentials will be marked expired.");
465 credentials.SetExpiration(Aws::Utils::DateTime::Now());
466 }
467 }
468 else
469 {
470 credentials.SetExpiration(std::chrono::time_point<std::chrono::system_clock>::max());
471 }
472
473 AWS_LOGSTREAM_DEBUG(PROFILE_LOG_TAG, "Successfully pulled credentials from process credential with AccessKey: " << accessKey << ", Expiration:" << credentialsView.GetString("Expiration"));
474 return credentials;
475}
476
477