/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include #include /* aws ecc identity which contains the data needed to sign a Sigv4a AWS request */ struct aws_ecc_identity { struct aws_string *access_key_id; struct aws_string *session_token; struct aws_ecc_key_pair *ecc_key; }; /* aws credentials identity which contains the data needed to sign an authenticated AWS request */ struct aws_credentials_identity { struct aws_string *access_key_id; struct aws_string *secret_access_key; struct aws_string *session_token; }; /* aws_token identity contains only a token to represent token only identities like a bearer token. */ struct aws_token_identity { struct aws_string *token; }; enum aws_identity_type { AWS_CREDENTIALS_IDENTITY, TOKEN_IDENTITY, ANONYMOUS_IDENTITY, ECC_IDENTITY, }; /* * A structure that wraps the different types of credentials that the customer can provider to establish their * identity. */ struct aws_credentials { struct aws_allocator *allocator; struct aws_atomic_var ref_count; /* * A timepoint, in seconds since epoch, at which the credentials should no longer be used because they * will have expired. * * * The primary purpose of this value is to allow providers to communicate to the caching provider any * additional constraints on how the sourced credentials should be used (STS). After refreshing the cached * credentials, the caching provider uses the following calculation to determine the next requery time: * * next_requery_time = now + cached_expiration_config; * if (cached_creds->expiration_timepoint_seconds < next_requery_time) { * next_requery_time = cached_creds->expiration_timepoint_seconds; * * The cached provider may, at its discretion, use a smaller requery time to avoid edge-case scenarios where * credential expiration becomes a race condition. * * The following leaf providers always set this value to UINT64_MAX (indefinite): * static * environment * imds * profile_config* * * * - profile_config may invoke sts which will use a non-max value * * The following leaf providers set this value to a sensible timepoint: * sts - value is based on current time + options->duration_seconds * */ uint64_t expiration_timepoint_seconds; enum aws_identity_type identity_type; union { struct aws_credentials_identity credentials_identity; struct aws_token_identity token_identity; struct aws_ecc_identity ecc_identity; } identity; }; /* * Credentials API implementations */ struct aws_credentials *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) { if (access_key_id_cursor.ptr == NULL || access_key_id_cursor.len == 0) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } if (secret_access_key_cursor.ptr == NULL || secret_access_key_cursor.len == 0) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } struct aws_credentials *credentials = aws_mem_acquire(allocator, sizeof(struct aws_credentials)); if (credentials == NULL) { return NULL; } AWS_ZERO_STRUCT(*credentials); credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); credentials->identity_type = AWS_CREDENTIALS_IDENTITY; struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials_identity; credentials_identity->access_key_id = aws_string_new_from_array(allocator, access_key_id_cursor.ptr, access_key_id_cursor.len); if (credentials_identity->access_key_id == NULL) { goto error; } credentials_identity->secret_access_key = aws_string_new_from_array(allocator, secret_access_key_cursor.ptr, secret_access_key_cursor.len); if (credentials_identity->secret_access_key == NULL) { goto error; } if (session_token_cursor.ptr != NULL && session_token_cursor.len > 0) { credentials_identity->session_token = aws_string_new_from_array(allocator, session_token_cursor.ptr, session_token_cursor.len); if (credentials_identity->session_token == NULL) { goto error; } } credentials->expiration_timepoint_seconds = expiration_timepoint_seconds; return credentials; error: aws_credentials_release(credentials); return NULL; } struct aws_credentials *aws_credentials_new_anonymous(struct aws_allocator *allocator) { struct aws_credentials *credentials = aws_mem_calloc(allocator, 1, sizeof(struct aws_credentials)); credentials->allocator = allocator; credentials->identity_type = ANONYMOUS_IDENTITY; aws_atomic_init_int(&credentials->ref_count, 1); credentials->expiration_timepoint_seconds = UINT64_MAX; return credentials; } static void s_aws_credentials_destroy(struct aws_credentials *credentials) { if (credentials == NULL) { return; } switch (credentials->identity_type) { case AWS_CREDENTIALS_IDENTITY: aws_string_destroy(credentials->identity.credentials_identity.access_key_id); aws_string_destroy_secure(credentials->identity.credentials_identity.secret_access_key); aws_string_destroy_secure(credentials->identity.credentials_identity.session_token); break; case ECC_IDENTITY: aws_string_destroy(credentials->identity.ecc_identity.access_key_id); aws_string_destroy_secure(credentials->identity.ecc_identity.session_token); aws_ecc_key_pair_release(credentials->identity.ecc_identity.ecc_key); break; case TOKEN_IDENTITY: aws_string_destroy_secure(credentials->identity.token_identity.token); break; case ANONYMOUS_IDENTITY: break; } aws_mem_release(credentials->allocator, credentials); } void aws_credentials_acquire(const struct aws_credentials *credentials) { if (credentials == NULL) { return; } aws_atomic_fetch_add((struct aws_atomic_var *)&credentials->ref_count, 1); } void aws_credentials_release(const struct aws_credentials *credentials) { if (credentials == NULL) { return; } size_t old_value = aws_atomic_fetch_sub((struct aws_atomic_var *)&credentials->ref_count, 1); if (old_value == 1) { s_aws_credentials_destroy((struct aws_credentials *)credentials); } } static struct aws_byte_cursor s_empty_token_cursor = { .ptr = NULL, .len = 0, }; struct aws_byte_cursor aws_credentials_get_access_key_id(const struct aws_credentials *credentials) { switch (credentials->identity_type) { case AWS_CREDENTIALS_IDENTITY: if (credentials->identity.credentials_identity.access_key_id != NULL) { return aws_byte_cursor_from_string(credentials->identity.credentials_identity.access_key_id); } break; case ECC_IDENTITY: if (credentials->identity.ecc_identity.access_key_id != NULL) { return aws_byte_cursor_from_string(credentials->identity.ecc_identity.access_key_id); } break; default: break; } return s_empty_token_cursor; } struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_credentials *credentials) { switch (credentials->identity_type) { case AWS_CREDENTIALS_IDENTITY: if (credentials->identity.credentials_identity.secret_access_key != NULL) { return aws_byte_cursor_from_string(credentials->identity.credentials_identity.secret_access_key); } break; default: break; } return s_empty_token_cursor; } struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_credentials *credentials) { switch (credentials->identity_type) { case AWS_CREDENTIALS_IDENTITY: if (credentials->identity.credentials_identity.session_token != NULL) { return aws_byte_cursor_from_string(credentials->identity.credentials_identity.session_token); } break; case ECC_IDENTITY: if (credentials->identity.ecc_identity.session_token != NULL) { return aws_byte_cursor_from_string(credentials->identity.ecc_identity.session_token); } break; default: break; } return s_empty_token_cursor; } struct aws_byte_cursor aws_credentials_get_token(const struct aws_credentials *credentials) { switch (credentials->identity_type) { case TOKEN_IDENTITY: if (credentials->identity.token_identity.token != NULL) { return aws_byte_cursor_from_string(credentials->identity.token_identity.token); } break; default: break; } return s_empty_token_cursor; } uint64_t aws_credentials_get_expiration_timepoint_seconds(const struct aws_credentials *credentials) { return credentials->expiration_timepoint_seconds; } struct aws_ecc_key_pair *aws_credentials_get_ecc_key_pair(const struct aws_credentials *credentials) { if (credentials->identity_type == ECC_IDENTITY) { return credentials->identity.ecc_identity.ecc_key; } return NULL; } bool aws_credentials_is_anonymous(const struct aws_credentials *credentials) { AWS_PRECONDITION(credentials); return credentials->identity_type == ANONYMOUS_IDENTITY; } struct aws_credentials *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) { struct aws_byte_cursor access_key_cursor = aws_byte_cursor_from_string(access_key_id); struct aws_byte_cursor secret_access_key_cursor = aws_byte_cursor_from_string(secret_access_key); struct aws_byte_cursor session_token_cursor; AWS_ZERO_STRUCT(session_token_cursor); if (session_token) { session_token_cursor = aws_byte_cursor_from_string(session_token); } return aws_credentials_new( allocator, access_key_cursor, secret_access_key_cursor, session_token_cursor, expiration_timepoint_seconds); } struct aws_credentials *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) { if (access_key_id.len == 0 || ecc_key == NULL) { AWS_LOGF_ERROR(AWS_LS_AUTH_GENERAL, "Provided credentials do not have a valid access_key_id or ecc_key"); return NULL; } struct aws_credentials *credentials = aws_mem_calloc(allocator, 1, sizeof(struct aws_credentials)); if (credentials == NULL) { return NULL; } credentials->allocator = allocator; credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds; aws_atomic_init_int(&credentials->ref_count, 1); aws_ecc_key_pair_acquire(ecc_key); credentials->identity_type = ECC_IDENTITY; credentials->identity.ecc_identity.ecc_key = ecc_key; credentials->identity.ecc_identity.access_key_id = aws_string_new_from_array(allocator, access_key_id.ptr, access_key_id.len); if (credentials->identity.ecc_identity.access_key_id == NULL) { goto on_error; } if (session_token.ptr != NULL && session_token.len > 0) { credentials->identity.ecc_identity.session_token = aws_string_new_from_array(allocator, session_token.ptr, session_token.len); if (credentials->identity.ecc_identity.session_token == NULL) { goto on_error; } } return credentials; on_error: s_aws_credentials_destroy(credentials); return NULL; } struct aws_credentials *aws_credentials_new_ecc_from_aws_credentials( struct aws_allocator *allocator, const struct aws_credentials *credentials) { struct aws_ecc_key_pair *ecc_key = aws_ecc_key_pair_new_ecdsa_p256_key_from_aws_credentials(allocator, credentials); if (ecc_key == NULL) { return NULL; } struct aws_credentials *ecc_credentials = aws_credentials_new_ecc( allocator, aws_credentials_get_access_key_id(credentials), ecc_key, aws_credentials_get_session_token(credentials), aws_credentials_get_expiration_timepoint_seconds(credentials)); aws_ecc_key_pair_release(ecc_key); return ecc_credentials; } struct aws_credentials *aws_credentials_new_token( struct aws_allocator *allocator, struct aws_byte_cursor token, uint64_t expiration_timepoint_in_seconds) { if (token.ptr == NULL || token.len == 0) { aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } struct aws_credentials *credentials = aws_mem_calloc(allocator, 1, sizeof(struct aws_credentials)); credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); credentials->identity_type = TOKEN_IDENTITY; struct aws_token_identity *token_identity = &credentials->identity.token_identity; token_identity->token = aws_string_new_from_array(allocator, token.ptr, token.len); credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds; return credentials; } /* * global credentials provider APIs */ void aws_credentials_provider_destroy(struct aws_credentials_provider *provider) { if (provider != NULL) { provider->vtable->destroy(provider); } } struct aws_credentials_provider *aws_credentials_provider_release(struct aws_credentials_provider *provider) { if (provider == NULL) { return NULL; } size_t old_value = aws_atomic_fetch_sub(&provider->ref_count, 1); if (old_value == 1) { aws_credentials_provider_destroy(provider); } return NULL; } struct aws_credentials_provider *aws_credentials_provider_acquire(struct aws_credentials_provider *provider) { if (provider == NULL) { return NULL; } aws_atomic_fetch_add(&provider->ref_count, 1); return provider; } int aws_credentials_provider_get_credentials( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, void *user_data) { AWS_ASSERT(provider->vtable->get_credentials); return provider->vtable->get_credentials(provider, callback, user_data); }