Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************** |
2 | | * |
3 | | * Name: cpl_aws.h |
4 | | * Project: CPL - Common Portability Library |
5 | | * Purpose: Amazon Web Services routines |
6 | | * Author: Even Rouault <even.rouault at spatialys.com> |
7 | | * |
8 | | ********************************************************************** |
9 | | * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #ifndef CPL_AWS_INCLUDED_H |
15 | | #define CPL_AWS_INCLUDED_H |
16 | | |
17 | | #ifndef DOXYGEN_SKIP |
18 | | |
19 | | #ifdef HAVE_CURL |
20 | | |
21 | | #include <cstddef> |
22 | | #include <mutex> |
23 | | |
24 | | #include "cpl_string.h" |
25 | | |
26 | | #include <curl/curl.h> |
27 | | #include <map> |
28 | | |
29 | | std::string CPLGetLowerCaseHexSHA256(const void *pabyData, size_t nBytes); |
30 | | std::string CPLGetLowerCaseHexSHA256(const std::string &osStr); |
31 | | |
32 | | std::string CPLGetAWS_SIGN4_Timestamp(GIntBig timestamp); |
33 | | |
34 | | std::string CPLAWSURLEncode(const std::string &osURL, bool bEncodeSlash = true); |
35 | | |
36 | | std::string CPLAWSGetHeaderVal(const struct curl_slist *psExistingHeaders, |
37 | | const char *pszKey); |
38 | | |
39 | | class IVSIS3LikeHandleHelper |
40 | | { |
41 | | CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeHandleHelper) |
42 | | |
43 | | protected: |
44 | | std::map<std::string, std::string> m_oMapQueryParameters{}; |
45 | | |
46 | | virtual void RebuildURL() = 0; |
47 | | std::string GetQueryString(bool bAddEmptyValueAfterEqual) const; |
48 | | |
49 | | public: |
50 | | IVSIS3LikeHandleHelper(); |
51 | | virtual ~IVSIS3LikeHandleHelper(); |
52 | | |
53 | | void ResetQueryParameters(); |
54 | | void AddQueryParameter(const std::string &osKey, |
55 | | const std::string &osValue); |
56 | | |
57 | | virtual struct curl_slist * |
58 | | GetCurlHeaders(const std::string &osVerb, struct curl_slist *psHeaders, |
59 | | const void *pabyDataContent = nullptr, |
60 | | size_t nBytesContent = 0) const = 0; |
61 | | |
62 | | virtual bool AllowAutomaticRedirection() |
63 | 0 | { |
64 | 0 | return true; |
65 | 0 | } |
66 | | |
67 | | virtual bool CanRestartOnError(const char *, const char * /* pszHeaders*/, |
68 | | bool /*bSetError*/) |
69 | 4.96k | { |
70 | 4.96k | return false; |
71 | 4.96k | } |
72 | | |
73 | | virtual const std::string &GetURL() const = 0; |
74 | | std::string GetURLNoKVP() const; |
75 | | |
76 | | virtual std::string GetCopySourceHeader() const |
77 | 0 | { |
78 | 0 | return std::string(); |
79 | 0 | } |
80 | | |
81 | | virtual const char *GetMetadataDirectiveREPLACE() const |
82 | 0 | { |
83 | 0 | return ""; |
84 | 0 | } |
85 | | |
86 | | static bool GetBucketAndObjectKey(const char *pszURI, |
87 | | const char *pszFSPrefix, |
88 | | bool bAllowNoObject, |
89 | | std::string &osBucketOut, |
90 | | std::string &osObjectKeyOut); |
91 | | |
92 | | static std::string BuildCanonicalizedHeaders( |
93 | | std::map<std::string, std::string> &oSortedMapHeaders, |
94 | | const struct curl_slist *psExistingHeaders, |
95 | | const char *pszHeaderPrefix); |
96 | | |
97 | | static std::string GetRFC822DateTime(); |
98 | | }; |
99 | | |
100 | | enum class AWSCredentialsSource |
101 | | { |
102 | | REGULAR, // credentials from env variables or ~/.aws/crediential |
103 | | EC2, // credentials from EC2 private networking |
104 | | WEB_IDENTITY, // credentials from Web Identity Token |
105 | | // See |
106 | | // https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html |
107 | | ASSUMED_ROLE, // credentials from an STS assumed role |
108 | | // See |
109 | | // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-cli.html |
110 | | // and |
111 | | // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html |
112 | | SSO, // credentials from Single-Sign On |
113 | | }; |
114 | | |
115 | | class VSIS3HandleHelper final : public IVSIS3LikeHandleHelper |
116 | | { |
117 | | CPL_DISALLOW_COPY_ASSIGN(VSIS3HandleHelper) |
118 | | |
119 | | std::string m_osURL{}; |
120 | | std::string m_osService{}; |
121 | | mutable std::string m_osSecretAccessKey{}; |
122 | | mutable std::string m_osAccessKeyId{}; |
123 | | mutable std::string m_osSessionToken{}; |
124 | | std::string m_osS3SessionToken{}; |
125 | | std::string m_osEndpoint{}; |
126 | | std::string m_osRegion{}; |
127 | | std::string m_osRequestPayer{}; |
128 | | std::string m_osBucket{}; |
129 | | std::string m_osObjectKey{}; |
130 | | bool m_bUseHTTPS = false; |
131 | | bool m_bUseVirtualHosting = false; |
132 | | bool m_bIsDirectoryBucket = false; |
133 | | AWSCredentialsSource m_eCredentialsSource = AWSCredentialsSource::REGULAR; |
134 | | |
135 | | void RebuildURL() override; |
136 | | |
137 | | static bool GetOrRefreshTemporaryCredentialsForRole( |
138 | | bool bForceRefresh, std::string &osSecretAccessKey, |
139 | | std::string &osAccessKeyId, std::string &osSessionToken, |
140 | | std::string &osRegion); |
141 | | |
142 | | static bool GetOrRefreshTemporaryCredentialsForSSO( |
143 | | bool bForceRefresh, std::string &osSecretAccessKey, |
144 | | std::string &osAccessKeyId, std::string &osSessionToken, |
145 | | std::string &osRegion); |
146 | | |
147 | | static bool GetConfigurationFromAssumeRoleWithWebIdentity( |
148 | | bool bForceRefresh, const std::string &osPathForOption, |
149 | | const std::string &osRoleArnIn, |
150 | | const std::string &osWebIdentityTokenFileIn, |
151 | | std::string &osSecretAccessKey, std::string &osAccessKeyId, |
152 | | std::string &osSessionToken); |
153 | | |
154 | | static bool GetConfigurationFromEC2(bool bForceRefresh, |
155 | | const std::string &osPathForOption, |
156 | | std::string &osSecretAccessKey, |
157 | | std::string &osAccessKeyId, |
158 | | std::string &osSessionToken); |
159 | | |
160 | | static bool GetConfigurationFromAWSConfigFiles( |
161 | | const std::string &osPathForOption, const char *pszProfile, |
162 | | std::string &osSecretAccessKey, std::string &osAccessKeyId, |
163 | | std::string &osSessionToken, std::string &osRegion, |
164 | | std::string &osCredentials, std::string &osRoleArn, |
165 | | std::string &osSourceProfile, std::string &osExternalId, |
166 | | std::string &osMFASerial, std::string &osRoleSessionName, |
167 | | std::string &osWebIdentityTokenFile, std::string &osSSOStartURL, |
168 | | std::string &osSSOAccountID, std::string &osSSORoleName, |
169 | | std::string &osSSOSession); |
170 | | |
171 | | static bool GetConfiguration(const std::string &osPathForOption, |
172 | | CSLConstList papszOptions, |
173 | | std::string &osSecretAccessKey, |
174 | | std::string &osAccessKeyId, |
175 | | std::string &osSessionToken, |
176 | | std::string &osRegion, |
177 | | AWSCredentialsSource &eCredentialsSource); |
178 | | |
179 | | void RefreshCredentials(const std::string &osPathForOption, |
180 | | bool bForceRefresh) const; |
181 | | |
182 | | protected: |
183 | | public: |
184 | | VSIS3HandleHelper( |
185 | | const std::string &osService, const std::string &osSecretAccessKey, |
186 | | const std::string &osAccessKeyId, const std::string &osSessionToken, |
187 | | const std::string &osS3SessionToken, const std::string &osEndpoint, |
188 | | const std::string &osRegion, const std::string &osRequestPayer, |
189 | | const std::string &osBucket, const std::string &osObjectKey, |
190 | | bool bUseHTTPS, bool bUseVirtualHosting, |
191 | | AWSCredentialsSource eCredentialsSource, bool bIsDirectoryBucket); |
192 | | ~VSIS3HandleHelper(); |
193 | | |
194 | | static VSIS3HandleHelper *BuildFromURI(const char *pszURI, |
195 | | const char *pszFSPrefix, |
196 | | bool bAllowNoObject, |
197 | | CSLConstList papszOptions = nullptr); |
198 | | static std::string BuildURL(const std::string &osEndpoint, |
199 | | const std::string &osBucket, |
200 | | const std::string &osObjectKey, bool bUseHTTPS, |
201 | | bool bUseVirtualHosting); |
202 | | |
203 | | struct curl_slist *GetCurlHeaders(const std::string &osVerb, |
204 | | struct curl_slist *psHeaders, |
205 | | const void *pabyDataContent = nullptr, |
206 | | size_t nBytesContent = 0) const override; |
207 | | |
208 | | bool IsDirectoryBucket() const |
209 | 0 | { |
210 | 0 | return m_bIsDirectoryBucket; |
211 | 0 | } |
212 | | |
213 | | bool AllowAutomaticRedirection() override |
214 | 0 | { |
215 | 0 | return false; |
216 | 0 | } |
217 | | |
218 | | bool CanRestartOnError(const char *, const char *pszHeaders, |
219 | | bool bSetError) override; |
220 | | |
221 | | const std::string &GetURL() const override |
222 | 0 | { |
223 | 0 | return m_osURL; |
224 | 0 | } |
225 | | |
226 | | const std::string &GetBucket() const |
227 | 0 | { |
228 | 0 | return m_osBucket; |
229 | 0 | } |
230 | | |
231 | | const std::string &GetObjectKey() const |
232 | 0 | { |
233 | 0 | return m_osObjectKey; |
234 | 0 | } |
235 | | |
236 | | const std::string &GetEndpoint() const |
237 | 0 | { |
238 | 0 | return m_osEndpoint; |
239 | 0 | } |
240 | | |
241 | | const std::string &GetRegion() const |
242 | 0 | { |
243 | 0 | return m_osRegion; |
244 | 0 | } |
245 | | |
246 | | const std::string &GetRequestPayer() const |
247 | 0 | { |
248 | 0 | return m_osRequestPayer; |
249 | 0 | } |
250 | | |
251 | | bool GetVirtualHosting() const |
252 | 0 | { |
253 | 0 | return m_bUseVirtualHosting; |
254 | 0 | } |
255 | | |
256 | | void SetEndpoint(const std::string &osStr); |
257 | | void SetRegion(const std::string &osStr); |
258 | | void SetRequestPayer(const std::string &osStr); |
259 | | void SetVirtualHosting(bool b); |
260 | | |
261 | | std::string GetCopySourceHeader() const override |
262 | 0 | { |
263 | 0 | return "x-amz-copy-source"; |
264 | 0 | } |
265 | | |
266 | | const char *GetMetadataDirectiveREPLACE() const override |
267 | 0 | { |
268 | 0 | return "x-amz-metadata-directive: REPLACE"; |
269 | 0 | } |
270 | | |
271 | | std::string GetSignedURL(CSLConstList papszOptions); |
272 | | |
273 | | static void CleanMutex(); |
274 | | static void ClearCache(); |
275 | | }; |
276 | | |
277 | | class VSIS3UpdateParams |
278 | | { |
279 | | private: |
280 | | std::string m_osRegion{}; |
281 | | std::string m_osEndpoint{}; |
282 | | std::string m_osRequestPayer{}; |
283 | | bool m_bUseVirtualHosting = false; |
284 | | |
285 | | explicit VSIS3UpdateParams(const VSIS3HandleHelper *poHelper) |
286 | 0 | : m_osRegion(poHelper->GetRegion()), |
287 | 0 | m_osEndpoint(poHelper->GetEndpoint()), |
288 | 0 | m_osRequestPayer(poHelper->GetRequestPayer()), |
289 | 0 | m_bUseVirtualHosting(poHelper->GetVirtualHosting()) |
290 | 0 | { |
291 | 0 | } |
292 | | |
293 | | void UpdateHandlerHelper(VSIS3HandleHelper *poHelper) |
294 | 0 | { |
295 | 0 | poHelper->SetRegion(m_osRegion); |
296 | 0 | poHelper->SetEndpoint(m_osEndpoint); |
297 | 0 | poHelper->SetRequestPayer(m_osRequestPayer); |
298 | 0 | poHelper->SetVirtualHosting(m_bUseVirtualHosting); |
299 | 0 | } |
300 | | |
301 | | static std::mutex gsMutex; |
302 | | static std::map<std::string, VSIS3UpdateParams> goMapBucketsToS3Params; |
303 | | |
304 | | public: |
305 | 0 | VSIS3UpdateParams() = default; |
306 | | |
307 | | static void UpdateMapFromHandle(VSIS3HandleHelper *poS3HandleHelper); |
308 | | static void UpdateHandleFromMap(VSIS3HandleHelper *poS3HandleHelper); |
309 | | static void ClearCache(); |
310 | | }; |
311 | | |
312 | | #endif /* HAVE_CURL */ |
313 | | |
314 | | #endif /* #ifndef DOXYGEN_SKIP */ |
315 | | |
316 | | #endif /* CPL_AWS_INCLUDED_H */ |