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#include <aws/core/config/AWSProfileConfigLoader.h>
17#include <aws/core/internal/AWSHttpResourceClient.h>
18#include <aws/core/auth/AWSCredentialsProvider.h>
19#include <aws/core/utils/memory/stl/AWSList.h>
20#include <aws/core/utils/memory/stl/AWSStreamFwd.h>
21#include <aws/core/utils/StringUtils.h>
22#include <aws/core/utils/logging/LogMacros.h>
23#include <aws/core/utils/json/JsonSerializer.h>
24#include <fstream>
25
26namespace Aws
27{
28 namespace Config
29 {
30 using namespace Aws::Utils;
31 using namespace Aws::Auth;
32
33 static const char* const CONFIG_LOADER_TAG = "Aws::Config::AWSProfileConfigLoader";
34 #ifdef _MSC_VER
35 // VS2015 compiler's bug, warning s_CoreErrorsMapper: symbol will be dynamically initialized (implementation limitation)
36 AWS_SUPPRESS_WARNING(4592,
37 static Aws::UniquePtr<ConfigAndCredentialsCacheManager> s_configManager(nullptr);
38 )
39 #else
40 static Aws::UniquePtr<ConfigAndCredentialsCacheManager> s_configManager(nullptr);
41 #endif
42
43 static const char CONFIG_CREDENTIALS_CACHE_MANAGER_TAG[] = "ConfigAndCredentialsCacheManager";
44
45 bool AWSProfileConfigLoader::Load()
46 {
47 if(LoadInternal())
48 {
49 AWS_LOGSTREAM_INFO(CONFIG_LOADER_TAG, "Successfully reloaded configuration.");
50 m_lastLoadTime = DateTime::Now();
51 AWS_LOGSTREAM_TRACE(CONFIG_LOADER_TAG, "reloaded config at "
52 << m_lastLoadTime.ToGmtString(DateFormat::ISO_8601));
53 return true;
54 }
55
56 AWS_LOGSTREAM_INFO(CONFIG_LOADER_TAG, "Failed to reload configuration.");
57 return false;
58 }
59
60 bool AWSProfileConfigLoader::PersistProfiles(const Aws::Map<Aws::String, Profile>& profiles)
61 {
62 if(PersistInternal(profiles))
63 {
64 AWS_LOGSTREAM_INFO(CONFIG_LOADER_TAG, "Successfully persisted configuration.");
65 m_profiles = profiles;
66 m_lastLoadTime = DateTime::Now();
67 AWS_LOGSTREAM_TRACE(CONFIG_LOADER_TAG, "persisted config at "
68 << m_lastLoadTime.ToGmtString(DateFormat::ISO_8601));
69 return true;
70 }
71
72 AWS_LOGSTREAM_WARN(CONFIG_LOADER_TAG, "Failed to persist configuration.");
73 return false;
74 }
75
76 static const char REGION_KEY[] = "region";
77 static const char ACCESS_KEY_ID_KEY[] = "aws_access_key_id";
78 static const char SECRET_KEY_KEY[] = "aws_secret_access_key";
79 static const char SESSION_TOKEN_KEY[] = "aws_session_token";
80 static const char ROLE_ARN_KEY[] = "role_arn";
81 static const char EXTERNAL_ID_KEY[] = "external_id";
82 static const char CREDENTIAL_PROCESS_COMMAND[] = "credential_process";
83 static const char SOURCE_PROFILE_KEY[] = "source_profile";
84 static const char PROFILE_PREFIX[] = "profile ";
85 static const char EQ = '=';
86 static const char LEFT_BRACKET = '[';
87 static const char RIGHT_BRACKET = ']';
88 static const char PARSER_TAG[] = "Aws::Config::ConfigFileProfileFSM";
89
90 class ConfigFileProfileFSM
91 {
92 public:
93 ConfigFileProfileFSM() : m_parserState(START) {}
94
95 const Aws::Map<String, Profile>& GetProfiles() const { return m_foundProfiles; }
96
97 void ParseStream(Aws::IStream& stream)
98 {
99 static const size_t ASSUME_EMPTY_LEN = 3;
100
101 Aws::String line;
102 while(std::getline(stream, line) && m_parserState != FAILURE)
103 {
104 if (line.empty() || line.length() < ASSUME_EMPTY_LEN)
105 {
106 continue;
107 }
108
109 auto openPos = line.find(LEFT_BRACKET);
110 auto closePos = line.find(RIGHT_BRACKET);
111
112 switch(m_parserState)
113 {
114
115 case START:
116 if(openPos != std::string::npos && closePos != std::string::npos)
117 {
118 FlushProfileAndReset(line, openPos, closePos);
119 m_parserState = PROFILE_FOUND;
120 }
121 break;
122
123 //fallthrough here is intentional to reduce duplicate logic
124 case PROFILE_KEY_VALUE_FOUND:
125 if(openPos != std::string::npos && closePos != std::string::npos)
126 {
127 m_parserState = PROFILE_FOUND;
128 FlushProfileAndReset(line, openPos, closePos);
129 break;
130 }
131 // fall through
132 case PROFILE_FOUND:
133 {
134 auto equalsPos = line.find(EQ);
135 if (equalsPos != std::string::npos)
136 {
137 auto key = line.substr(0, equalsPos);
138 auto value = line.substr(equalsPos + 1);
139 m_profileKeyValuePairs[StringUtils::Trim(key.c_str())] =
140 StringUtils::Trim(value.c_str());
141 m_parserState = PROFILE_KEY_VALUE_FOUND;
142 }
143
144 break;
145 }
146 default:
147 m_parserState = FAILURE;
148 break;
149 }
150 }
151
152 FlushProfileAndReset(line, std::string::npos, std::string::npos);
153 }
154
155 private:
156
157 void FlushProfileAndReset(Aws::String& line, size_t openPos, size_t closePos)
158 {
159 if(!m_currentWorkingProfile.empty() && !m_profileKeyValuePairs.empty())
160 {
161 Profile profile;
162 profile.SetName(m_currentWorkingProfile);
163
164 auto regionIter = m_profileKeyValuePairs.find(REGION_KEY);
165 if (regionIter != m_profileKeyValuePairs.end())
166 {
167 AWS_LOGSTREAM_DEBUG(PARSER_TAG, "found region " << regionIter->second);
168 profile.SetRegion(regionIter->second);
169 }
170
171 auto accessKeyIdIter = m_profileKeyValuePairs.find(ACCESS_KEY_ID_KEY);
172 Aws::String accessKey, secretKey, sessionToken;
173 if (accessKeyIdIter != m_profileKeyValuePairs.end())
174 {
175 accessKey = accessKeyIdIter->second;
176 AWS_LOGSTREAM_DEBUG(PARSER_TAG, "found access key " << accessKey);
177
178 auto secretAccessKeyIter = m_profileKeyValuePairs.find(SECRET_KEY_KEY);
179 auto sessionTokenIter = m_profileKeyValuePairs.find(SESSION_TOKEN_KEY);
180 if (secretAccessKeyIter != m_profileKeyValuePairs.end())
181 {
182 secretKey = secretAccessKeyIter->second;
183 }
184 else
185 {
186 AWS_LOGSTREAM_ERROR(PARSER_TAG, "No secret access key found even though an access key was specified. This will cause all signed AWS calls to fail.");
187 }
188
189 if (sessionTokenIter != m_profileKeyValuePairs.end())
190 {
191 sessionToken = sessionTokenIter->second;
192 }
193
194 profile.SetCredentials(Aws::Auth::AWSCredentials(accessKey, secretKey, sessionToken));
195 }
196
197 auto assumeRoleArnIter = m_profileKeyValuePairs.find(ROLE_ARN_KEY);
198 if (assumeRoleArnIter != m_profileKeyValuePairs.end())
199 {
200 AWS_LOGSTREAM_DEBUG(PARSER_TAG, "found role arn " << assumeRoleArnIter->second);
201 profile.SetRoleArn(assumeRoleArnIter->second);
202 }
203
204 auto externalIdIter = m_profileKeyValuePairs.find(EXTERNAL_ID_KEY);
205 if (externalIdIter != m_profileKeyValuePairs.end())
206 {
207 AWS_LOGSTREAM_DEBUG(PARSER_TAG, "found external id " << externalIdIter->second);
208 profile.SetExternalId(externalIdIter->second);
209 }
210
211 auto sourceProfileIter = m_profileKeyValuePairs.find(SOURCE_PROFILE_KEY);
212 if (sourceProfileIter != m_profileKeyValuePairs.end())
213 {
214 AWS_LOGSTREAM_DEBUG(PARSER_TAG, "found source profile " << sourceProfileIter->second);
215 profile.SetSourceProfile(sourceProfileIter->second);
216 }
217
218 auto credentialProcessIter = m_profileKeyValuePairs.find(CREDENTIAL_PROCESS_COMMAND);
219 if (credentialProcessIter != m_profileKeyValuePairs.end())
220 {
221 AWS_LOGSTREAM_DEBUG(PARSER_TAG, "found credential process " << credentialProcessIter->second);
222 profile.SetCredentialProcess(credentialProcessIter->second);
223 }
224 profile.SetAllKeyValPairs(m_profileKeyValuePairs);
225
226 m_foundProfiles[profile.GetName()] = std::move(profile);
227 m_currentWorkingProfile.clear();
228 m_profileKeyValuePairs.clear();
229 }
230
231 if(!line.empty() && openPos != std::string::npos && closePos != std::string::npos)
232 {
233 m_currentWorkingProfile = StringUtils::Trim(line.substr(openPos + 1, closePos - openPos - 1).c_str());
234 StringUtils::Replace(m_currentWorkingProfile, PROFILE_PREFIX, "");
235 AWS_LOGSTREAM_DEBUG(PARSER_TAG, "found profile " << m_currentWorkingProfile);
236 }
237 }
238
239 enum State
240 {
241 START = 0,
242 PROFILE_FOUND,
243 PROFILE_KEY_VALUE_FOUND,
244 FAILURE
245 };
246
247 Aws::String m_currentWorkingProfile;
248 Aws::Map<String, String> m_profileKeyValuePairs;
249 State m_parserState;
250 Aws::Map<String, Profile> m_foundProfiles;
251 };
252
253 static const char* const CONFIG_FILE_LOADER = "Aws::Config::AWSConfigFileProfileConfigLoader";
254
255 AWSConfigFileProfileConfigLoader::AWSConfigFileProfileConfigLoader(const Aws::String& fileName, bool useProfilePrefix) :
256 m_fileName(fileName), m_useProfilePrefix(useProfilePrefix)
257 {
258 AWS_LOGSTREAM_INFO(CONFIG_FILE_LOADER, "Initializing config loader against fileName "
259 << fileName << " and using profilePrefix = " << useProfilePrefix);
260 }
261
262 bool AWSConfigFileProfileConfigLoader::LoadInternal()
263 {
264 m_profiles.clear();
265
266 Aws::IFStream inputFile(m_fileName.c_str());
267 if(inputFile)
268 {
269 ConfigFileProfileFSM parser;
270 parser.ParseStream(inputFile);
271 m_profiles = parser.GetProfiles();
272 return m_profiles.size() > 0;
273 }
274
275 AWS_LOGSTREAM_INFO(CONFIG_FILE_LOADER, "Unable to open config file " << m_fileName << " for reading.");
276
277 return false;
278 }
279
280 bool AWSConfigFileProfileConfigLoader::PersistInternal(const Aws::Map<Aws::String, Profile>& profiles)
281 {
282 Aws::OFStream outputFile(m_fileName.c_str(), std::ios_base::out | std::ios_base::trunc);
283 if(outputFile)
284 {
285 for(auto& profile : profiles)
286 {
287 Aws::String prefix = m_useProfilePrefix ? PROFILE_PREFIX : "";
288
289 AWS_LOGSTREAM_DEBUG(CONFIG_FILE_LOADER, "Writing profile " << profile.first << " to disk.");
290
291 outputFile << LEFT_BRACKET << prefix << profile.second.GetName() << RIGHT_BRACKET << std::endl;
292 const Aws::Auth::AWSCredentials& credentials = profile.second.GetCredentials();
293 outputFile << ACCESS_KEY_ID_KEY << EQ << credentials.GetAWSAccessKeyId() << std::endl;
294 outputFile << SECRET_KEY_KEY << EQ << credentials.GetAWSSecretKey() << std::endl;
295
296 if(!credentials.GetSessionToken().empty())
297 {
298 outputFile << SESSION_TOKEN_KEY << EQ << credentials.GetSessionToken() << std::endl;
299 }
300
301 if(!profile.second.GetRegion().empty())
302 {
303 outputFile << REGION_KEY << EQ << profile.second.GetRegion() << std::endl;
304 }
305
306 if(!profile.second.GetRoleArn().empty())
307 {
308 outputFile << ROLE_ARN_KEY << EQ << profile.second.GetRoleArn() << std::endl;
309 }
310
311 if(!profile.second.GetSourceProfile().empty())
312 {
313 outputFile << SOURCE_PROFILE_KEY << EQ << profile.second.GetSourceProfile() << std::endl;
314 }
315
316 outputFile << std::endl;
317 }
318
319 AWS_LOGSTREAM_INFO(CONFIG_FILE_LOADER, "Profiles written to config file " << m_fileName);
320
321 return true;
322 }
323
324 AWS_LOGSTREAM_WARN(CONFIG_FILE_LOADER, "Unable to open config file " << m_fileName << " for writing.");
325
326 return false;
327 }
328
329 static const char* const EC2_INSTANCE_PROFILE_LOG_TAG = "Aws::Config::EC2InstanceProfileConfigLoader";
330
331 EC2InstanceProfileConfigLoader::EC2InstanceProfileConfigLoader(const std::shared_ptr<Aws::Internal::EC2MetadataClient>& client)
332 : m_ec2metadataClient(client == nullptr ? Aws::MakeShared<Aws::Internal::EC2MetadataClient>(EC2_INSTANCE_PROFILE_LOG_TAG) : client)
333 {
334 }
335
336 bool EC2InstanceProfileConfigLoader::LoadInternal()
337 {
338 auto credentialsStr = m_ec2metadataClient->GetDefaultCredentialsSecurely();
339 if(credentialsStr.empty()) return false;
340
341 Json::JsonValue credentialsDoc(credentialsStr);
342 if (!credentialsDoc.WasParseSuccessful())
343 {
344 AWS_LOGSTREAM_ERROR(EC2_INSTANCE_PROFILE_LOG_TAG,
345 "Failed to parse output from EC2MetadataService.");
346 return false;
347 }
348 const char* accessKeyId = "AccessKeyId";
349 const char* secretAccessKey = "SecretAccessKey";
350 Aws::String accessKey, secretKey, token;
351
352 auto credentialsView = credentialsDoc.View();
353 accessKey = credentialsView.GetString(accessKeyId);
354 AWS_LOGSTREAM_INFO(EC2_INSTANCE_PROFILE_LOG_TAG,
355 "Successfully pulled credentials from metadata service with access key " << accessKey);
356
357 secretKey = credentialsView.GetString(secretAccessKey);
358 token = credentialsView.GetString("Token");
359
360 auto region = m_ec2metadataClient->GetCurrentRegion();
361
362 Profile profile;
363 profile.SetCredentials(AWSCredentials(accessKey, secretKey, token));
364 profile.SetRegion(region);
365 profile.SetName(INSTANCE_PROFILE_KEY);
366
367 m_profiles[INSTANCE_PROFILE_KEY] = profile;
368
369 return true;
370 }
371
372 ConfigAndCredentialsCacheManager::ConfigAndCredentialsCacheManager() :
373 m_credentialsFileLoader(Aws::Auth::ProfileConfigFileAWSCredentialsProvider::GetCredentialsProfileFilename()),
374 m_configFileLoader(Aws::Auth::GetConfigProfileFilename(), true/*use profile prefix*/)
375 {
376 ReloadCredentialsFile();
377 ReloadConfigFile();
378 }
379
380 void ConfigAndCredentialsCacheManager::ReloadConfigFile()
381 {
382 Aws::Utils::Threading::WriterLockGuard guard(m_configLock);
383 m_configFileLoader.SetFileName(Aws::Auth::GetConfigProfileFilename());
384 m_configFileLoader.Load();
385 }
386
387 void ConfigAndCredentialsCacheManager::ReloadCredentialsFile()
388 {
389 Aws::Utils::Threading::WriterLockGuard guard(m_credentialsLock);
390 m_credentialsFileLoader.SetFileName(Aws::Auth::ProfileConfigFileAWSCredentialsProvider::GetCredentialsProfileFilename());
391 m_credentialsFileLoader.Load();
392 }
393
394 bool ConfigAndCredentialsCacheManager::HasConfigProfile(const Aws::String& profileName) const
395 {
396 Aws::Utils::Threading::ReaderLockGuard guard(m_configLock);
397 return (m_configFileLoader.GetProfiles().count(profileName) == 1);
398 }
399
400 Aws::Config::Profile ConfigAndCredentialsCacheManager::GetConfigProfile(const Aws::String& profileName) const
401 {
402 Aws::Utils::Threading::ReaderLockGuard guard(m_configLock);
403 const auto& profiles = m_configFileLoader.GetProfiles();
404 const auto &iter = profiles.find(profileName);
405 if (iter == profiles.end())
406 {
407 return {};
408 }
409 return iter->second;
410 }
411
412 Aws::Map<Aws::String, Aws::Config::Profile> ConfigAndCredentialsCacheManager::GetConfigProfiles() const
413 {
414 Aws::Utils::Threading::ReaderLockGuard guard(m_configLock);
415 return m_configFileLoader.GetProfiles();
416 }
417
418 Aws::String ConfigAndCredentialsCacheManager::GetConfig(const Aws::String& profileName, const Aws::String& key) const
419 {
420 Aws::Utils::Threading::ReaderLockGuard guard(m_configLock);
421 const auto& profiles = m_configFileLoader.GetProfiles();
422 const auto &iter = profiles.find(profileName);
423 if (iter == profiles.end())
424 {
425 return {};
426 }
427 return iter->second.GetValue(key);
428 }
429
430 bool ConfigAndCredentialsCacheManager::HasCredentialsProfile(const Aws::String& profileName) const
431 {
432 Aws::Utils::Threading::ReaderLockGuard guard(m_credentialsLock);
433 return (m_credentialsFileLoader.GetProfiles().count(profileName) == 1);
434 }
435
436 Aws::Config::Profile ConfigAndCredentialsCacheManager::GetCredentialsProfile(const Aws::String& profileName) const
437 {
438 Aws::Utils::Threading::ReaderLockGuard guard(m_credentialsLock);
439 const auto &profiles = m_credentialsFileLoader.GetProfiles();
440 const auto &iter = profiles.find(profileName);
441 if (iter == profiles.end())
442 {
443 return {};
444 }
445 return iter->second;
446 }
447
448 Aws::Auth::AWSCredentials ConfigAndCredentialsCacheManager::GetCredentials(const Aws::String& profileName) const
449 {
450 Aws::Utils::Threading::ReaderLockGuard guard(m_credentialsLock);
451 const auto& profiles = m_credentialsFileLoader.GetProfiles();
452 const auto &iter = profiles.find(profileName);
453 if (iter == profiles.end())
454 {
455 return {};
456 }
457 return iter->second.GetCredentials();
458 }
459
460 void InitConfigAndCredentialsCacheManager()
461 {
462 if (s_configManager)
463 {
464 return;
465 }
466 s_configManager = Aws::MakeUnique<ConfigAndCredentialsCacheManager>(CONFIG_CREDENTIALS_CACHE_MANAGER_TAG);
467 }
468
469 void CleanupConfigAndCredentialsCacheManager()
470 {
471 if (!s_configManager)
472 {
473 return;
474 }
475 s_configManager = nullptr;
476 }
477
478 void ReloadCachedConfigFile()
479 {
480 assert(s_configManager);
481 s_configManager->ReloadConfigFile();
482 }
483
484 void ReloadCachedCredentialsFile()
485 {
486 assert(s_configManager);
487 s_configManager->ReloadCredentialsFile();
488 }
489
490 bool HasCachedConfigProfile(const Aws::String& profileName)
491 {
492 assert(s_configManager);
493 return s_configManager->HasConfigProfile(profileName);
494 }
495
496 Aws::Config::Profile GetCachedConfigProfile(const Aws::String& profileName)
497 {
498 assert(s_configManager);
499 return s_configManager->GetConfigProfile(profileName);
500 }
501
502 Aws::Map<Aws::String, Aws::Config::Profile> GetCachedConfigProfiles()
503 {
504 assert(s_configManager);
505 return s_configManager->GetConfigProfiles();
506 }
507
508 Aws::String GetCachedConfigValue(const Aws::String &profileName, const Aws::String &key)
509 {
510 assert(s_configManager);
511 return s_configManager->GetConfig(profileName, key);
512 }
513
514 Aws::String GetCachedConfigValue(const Aws::String &key)
515 {
516 assert(s_configManager);
517 return s_configManager->GetConfig(Aws::Auth::GetConfigProfileName(), key);
518 }
519
520 bool HasCachedCredentialsProfile(const Aws::String& profileName)
521 {
522 assert(s_configManager);
523 return s_configManager->HasCredentialsProfile(profileName);
524 }
525
526 Aws::Config::Profile GetCachedCredentialsProfile(const Aws::String &profileName)
527 {
528 assert(s_configManager);
529 return s_configManager->GetCredentialsProfile(profileName);
530 }
531
532 Aws::Auth::AWSCredentials GetCachedCredentials(const Aws::String &profileName)
533 {
534 assert(s_configManager);
535 return s_configManager->GetCredentials(profileName);
536 }
537 } // Config namespace
538} // Aws namespace
539