1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include <aws/auth/credentials.h>
7 
8 #include <aws/cal/ecc.h>
9 #include <aws/common/environment.h>
10 #include <aws/common/string.h>
11 
12 /*
13  * A structure that wraps the public/private data needed to sign an authenticated AWS request
14  */
15 struct aws_credentials {
16     struct aws_allocator *allocator;
17 
18     struct aws_atomic_var ref_count;
19 
20     struct aws_string *access_key_id;
21     struct aws_string *secret_access_key;
22     struct aws_string *session_token;
23 
24     /*
25      * A timepoint, in seconds since epoch, at which the credentials should no longer be used because they
26      * will have expired.
27      *
28      *
29      * The primary purpose of this value is to allow providers to communicate to the caching provider any
30      * additional constraints on how the sourced credentials should be used (STS).  After refreshing the cached
31      * credentials, the caching provider uses the following calculation to determine the next requery time:
32      *
33      *   next_requery_time = now + cached_expiration_config;
34      *   if (cached_creds->expiration_timepoint_seconds < next_requery_time) {
35      *       next_requery_time = cached_creds->expiration_timepoint_seconds;
36      *
37      *  The cached provider may, at its discretion, use a smaller requery time to avoid edge-case scenarios where
38      *  credential expiration becomes a race condition.
39      *
40      * The following leaf providers always set this value to UINT64_MAX (indefinite):
41      *    static
42      *    environment
43      *    imds
44      *    profile_config*
45      *
46      *  * - profile_config may invoke sts which will use a non-max value
47      *
48      *  The following leaf providers set this value to a sensible timepoint:
49      *    sts - value is based on current time + options->duration_seconds
50      *
51      */
52     uint64_t expiration_timepoint_seconds;
53 
54     struct aws_ecc_key_pair *ecc_key;
55 };
56 
57 /*
58  * Credentials API implementations
59  */
aws_credentials_new(struct aws_allocator * allocator,struct aws_byte_cursor access_key_id_cursor,struct aws_byte_cursor secret_access_key_cursor,struct aws_byte_cursor session_token_cursor,uint64_t expiration_timepoint_seconds)60 struct aws_credentials *aws_credentials_new(
61     struct aws_allocator *allocator,
62     struct aws_byte_cursor access_key_id_cursor,
63     struct aws_byte_cursor secret_access_key_cursor,
64     struct aws_byte_cursor session_token_cursor,
65     uint64_t expiration_timepoint_seconds) {
66 
67     if (access_key_id_cursor.ptr == NULL || access_key_id_cursor.len == 0) {
68         aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
69         return NULL;
70     }
71 
72     if (secret_access_key_cursor.ptr == NULL || secret_access_key_cursor.len == 0) {
73         aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
74         return NULL;
75     }
76 
77     struct aws_credentials *credentials = aws_mem_acquire(allocator, sizeof(struct aws_credentials));
78     if (credentials == NULL) {
79         return NULL;
80     }
81 
82     AWS_ZERO_STRUCT(*credentials);
83 
84     credentials->allocator = allocator;
85     aws_atomic_init_int(&credentials->ref_count, 1);
86 
87     credentials->access_key_id =
88         aws_string_new_from_array(allocator, access_key_id_cursor.ptr, access_key_id_cursor.len);
89     if (credentials->access_key_id == NULL) {
90         goto error;
91     }
92 
93     credentials->secret_access_key =
94         aws_string_new_from_array(allocator, secret_access_key_cursor.ptr, secret_access_key_cursor.len);
95     if (credentials->secret_access_key == NULL) {
96         goto error;
97     }
98 
99     if (session_token_cursor.ptr != NULL && session_token_cursor.len > 0) {
100         credentials->session_token =
101             aws_string_new_from_array(allocator, session_token_cursor.ptr, session_token_cursor.len);
102         if (credentials->session_token == NULL) {
103             goto error;
104         }
105     }
106 
107     credentials->expiration_timepoint_seconds = expiration_timepoint_seconds;
108 
109     return credentials;
110 
111 error:
112 
113     aws_credentials_release(credentials);
114 
115     return NULL;
116 }
117 
s_aws_credentials_destroy(struct aws_credentials * credentials)118 static void s_aws_credentials_destroy(struct aws_credentials *credentials) {
119     if (credentials == NULL) {
120         return;
121     }
122 
123     if (credentials->access_key_id != NULL) {
124         aws_string_destroy(credentials->access_key_id);
125     }
126 
127     if (credentials->secret_access_key != NULL) {
128         aws_string_destroy_secure(credentials->secret_access_key);
129     }
130 
131     if (credentials->session_token != NULL) {
132         aws_string_destroy_secure(credentials->session_token);
133     }
134 
135     aws_ecc_key_pair_release(credentials->ecc_key);
136 
137     aws_mem_release(credentials->allocator, credentials);
138 }
139 
aws_credentials_acquire(const struct aws_credentials * credentials)140 void aws_credentials_acquire(const struct aws_credentials *credentials) {
141     if (credentials == NULL) {
142         return;
143     }
144 
145     aws_atomic_fetch_add((struct aws_atomic_var *)&credentials->ref_count, 1);
146 }
147 
aws_credentials_release(const struct aws_credentials * credentials)148 void aws_credentials_release(const struct aws_credentials *credentials) {
149     if (credentials == NULL) {
150         return;
151     }
152 
153     size_t old_value = aws_atomic_fetch_sub((struct aws_atomic_var *)&credentials->ref_count, 1);
154     if (old_value == 1) {
155         s_aws_credentials_destroy((struct aws_credentials *)credentials);
156     }
157 }
158 
aws_credentials_get_access_key_id(const struct aws_credentials * credentials)159 struct aws_byte_cursor aws_credentials_get_access_key_id(const struct aws_credentials *credentials) {
160     return aws_byte_cursor_from_string(credentials->access_key_id);
161 }
162 
aws_credentials_get_secret_access_key(const struct aws_credentials * credentials)163 struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_credentials *credentials) {
164     return aws_byte_cursor_from_string(credentials->secret_access_key);
165 }
166 
167 static struct aws_byte_cursor s_empty_session_token_cursor = {
168     .ptr = NULL,
169     .len = 0,
170 };
171 
aws_credentials_get_session_token(const struct aws_credentials * credentials)172 struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_credentials *credentials) {
173     if (credentials->session_token != NULL) {
174         return aws_byte_cursor_from_string(credentials->session_token);
175     }
176 
177     return s_empty_session_token_cursor;
178 }
179 
aws_credentials_get_expiration_timepoint_seconds(const struct aws_credentials * credentials)180 uint64_t aws_credentials_get_expiration_timepoint_seconds(const struct aws_credentials *credentials) {
181     return credentials->expiration_timepoint_seconds;
182 }
183 
aws_credentials_get_ecc_key_pair(const struct aws_credentials * credentials)184 struct aws_ecc_key_pair *aws_credentials_get_ecc_key_pair(const struct aws_credentials *credentials) {
185     return credentials->ecc_key;
186 }
187 
aws_credentials_new_from_string(struct aws_allocator * allocator,const struct aws_string * access_key_id,const struct aws_string * secret_access_key,const struct aws_string * session_token,uint64_t expiration_timepoint_seconds)188 struct aws_credentials *aws_credentials_new_from_string(
189     struct aws_allocator *allocator,
190     const struct aws_string *access_key_id,
191     const struct aws_string *secret_access_key,
192     const struct aws_string *session_token,
193     uint64_t expiration_timepoint_seconds) {
194     struct aws_byte_cursor access_key_cursor = aws_byte_cursor_from_string(access_key_id);
195     struct aws_byte_cursor secret_access_key_cursor = aws_byte_cursor_from_string(secret_access_key);
196     struct aws_byte_cursor session_token_cursor;
197     AWS_ZERO_STRUCT(session_token_cursor);
198 
199     if (session_token) {
200         session_token_cursor = aws_byte_cursor_from_string(session_token);
201     }
202 
203     return aws_credentials_new(
204         allocator, access_key_cursor, secret_access_key_cursor, session_token_cursor, expiration_timepoint_seconds);
205 }
206 
aws_credentials_new_ecc(struct aws_allocator * allocator,struct aws_byte_cursor access_key_id,struct aws_ecc_key_pair * ecc_key,struct aws_byte_cursor session_token,uint64_t expiration_timepoint_in_seconds)207 struct aws_credentials *aws_credentials_new_ecc(
208     struct aws_allocator *allocator,
209     struct aws_byte_cursor access_key_id,
210     struct aws_ecc_key_pair *ecc_key,
211     struct aws_byte_cursor session_token,
212     uint64_t expiration_timepoint_in_seconds) {
213 
214     if (access_key_id.len == 0 || ecc_key == NULL) {
215         AWS_LOGF_ERROR(AWS_LS_AUTH_GENERAL, "Provided credentials do not have a valid access_key_id or ecc_key");
216         return NULL;
217     }
218 
219     struct aws_credentials *credentials = aws_mem_calloc(allocator, 1, sizeof(struct aws_credentials));
220     if (credentials == NULL) {
221         return NULL;
222     }
223 
224     credentials->allocator = allocator;
225     credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds;
226     aws_atomic_init_int(&credentials->ref_count, 1);
227     aws_ecc_key_pair_acquire(ecc_key);
228     credentials->ecc_key = ecc_key;
229 
230     credentials->access_key_id = aws_string_new_from_array(allocator, access_key_id.ptr, access_key_id.len);
231     if (credentials->access_key_id == NULL) {
232         goto on_error;
233     }
234 
235     if (session_token.ptr != NULL && session_token.len > 0) {
236         credentials->session_token = aws_string_new_from_array(allocator, session_token.ptr, session_token.len);
237         if (credentials->session_token == NULL) {
238             goto on_error;
239         }
240     }
241 
242     return credentials;
243 
244 on_error:
245 
246     s_aws_credentials_destroy(credentials);
247 
248     return NULL;
249 }
250 
aws_credentials_new_ecc_from_aws_credentials(struct aws_allocator * allocator,const struct aws_credentials * credentials)251 struct aws_credentials *aws_credentials_new_ecc_from_aws_credentials(
252     struct aws_allocator *allocator,
253     const struct aws_credentials *credentials) {
254 
255     struct aws_ecc_key_pair *ecc_key = aws_ecc_key_pair_new_ecdsa_p256_key_from_aws_credentials(allocator, credentials);
256 
257     if (ecc_key == NULL) {
258         return NULL;
259     }
260 
261     struct aws_credentials *ecc_credentials = aws_credentials_new_ecc(
262         allocator,
263         aws_credentials_get_access_key_id(credentials),
264         ecc_key,
265         aws_credentials_get_session_token(credentials),
266         aws_credentials_get_expiration_timepoint_seconds(credentials));
267 
268     aws_ecc_key_pair_release(ecc_key);
269 
270     return ecc_credentials;
271 }
272 
273 /*
274  * global credentials provider APIs
275  */
276 
aws_credentials_provider_destroy(struct aws_credentials_provider * provider)277 void aws_credentials_provider_destroy(struct aws_credentials_provider *provider) {
278     if (provider != NULL) {
279         provider->vtable->destroy(provider);
280     }
281 }
282 
aws_credentials_provider_release(struct aws_credentials_provider * provider)283 void aws_credentials_provider_release(struct aws_credentials_provider *provider) {
284     if (provider == NULL) {
285         return;
286     }
287 
288     size_t old_value = aws_atomic_fetch_sub(&provider->ref_count, 1);
289     if (old_value == 1) {
290         aws_credentials_provider_destroy(provider);
291     }
292 }
293 
aws_credentials_provider_acquire(struct aws_credentials_provider * provider)294 void aws_credentials_provider_acquire(struct aws_credentials_provider *provider) {
295     aws_atomic_fetch_add(&provider->ref_count, 1);
296 }
297 
aws_credentials_provider_get_credentials(struct aws_credentials_provider * provider,aws_on_get_credentials_callback_fn callback,void * user_data)298 int aws_credentials_provider_get_credentials(
299     struct aws_credentials_provider *provider,
300     aws_on_get_credentials_callback_fn callback,
301     void *user_data) {
302 
303     AWS_ASSERT(provider->vtable->get_credentials);
304 
305     return provider->vtable->get_credentials(provider, callback, user_data);
306 }
307