/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mongocrypt-private.h" #include "mongocrypt-binary-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-ctx-private.h" #include "mongocrypt-kms-ctx-private.h" #include "mongocrypt-opts-private.h" #include "mongocrypt-status-private.h" #include "mongocrypt-util-private.h" #include #include #include #include "mongocrypt.h" /* Sadly, Windows does not define SSIZE_MAX. It is defined in bson-compat.h, * but only since 1.22.x, so copy this from bson-compat.h for now. */ #ifndef SSIZE_MAX #define SSIZE_MAX \ (ssize_t) ( \ (((size_t) 0x01u) << (sizeof (ssize_t) * (size_t) CHAR_BIT - 1u)) - 1u) #endif typedef struct { mongocrypt_status_t *status; void *ctx; } ctx_with_status_t; /* Before we've read the Content-Length header in an HTTP response, * we don't know how many bytes we'll need. So return this value * in kms_ctx_bytes_needed until we are fed the Content-Length. */ #define DEFAULT_MAX_KMS_BYTE_REQUEST 1024 #define SHA256_LEN 32 #define DEFAULT_HTTPS_PORT "443" #define DEFAULT_KMIP_PORT "5696" static bool _sha256 (void *ctx, const char *input, size_t len, unsigned char *hash_out) { BSON_ASSERT_PARAM (ctx); BSON_ASSERT_PARAM (input); BSON_ASSERT_PARAM (hash_out); bool ret; ctx_with_status_t *ctx_with_status = (ctx_with_status_t *) ctx; _mongocrypt_crypto_t *crypto; mongocrypt_binary_t *plaintext, *out; crypto = (_mongocrypt_crypto_t *) ctx_with_status->ctx; BSON_ASSERT (crypto); BSON_ASSERT (len <= UINT32_MAX); plaintext = mongocrypt_binary_new_from_data ((uint8_t *) input, (uint32_t) len); out = mongocrypt_binary_new (); out->data = hash_out; out->len = SHA256_LEN; ret = crypto->sha_256 (crypto->ctx, plaintext, out, ctx_with_status->status); mongocrypt_binary_destroy (plaintext); mongocrypt_binary_destroy (out); return ret; } static bool _sha256_hmac (void *ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out) { BSON_ASSERT_PARAM (ctx); BSON_ASSERT_PARAM (key_input); BSON_ASSERT_PARAM (input); BSON_ASSERT_PARAM (hash_out); ctx_with_status_t *ctx_with_status = (ctx_with_status_t *) ctx; _mongocrypt_crypto_t *crypto; mongocrypt_binary_t *key, *plaintext, *out; bool ret; crypto = (_mongocrypt_crypto_t *) ctx_with_status->ctx; BSON_ASSERT (crypto); BSON_ASSERT (key_len <= UINT32_MAX); key = mongocrypt_binary_new_from_data ((uint8_t *) key_input, (uint32_t) key_len); BSON_ASSERT (len <= UINT32_MAX); plaintext = mongocrypt_binary_new_from_data ((uint8_t *) input, (uint32_t) len); out = mongocrypt_binary_new (); out->data = hash_out; out->len = SHA256_LEN; ret = crypto->hmac_sha_256 ( crypto->ctx, key, plaintext, out, ctx_with_status->status); mongocrypt_binary_destroy (key); mongocrypt_binary_destroy (plaintext); mongocrypt_binary_destroy (out); return ret; } static void _set_kms_crypto_hooks (_mongocrypt_crypto_t *crypto, ctx_with_status_t *ctx_with_status, kms_request_opt_t *opts) { BSON_ASSERT_PARAM (crypto); BSON_ASSERT_PARAM (ctx_with_status); BSON_ASSERT_PARAM (opts); if (crypto->hooks_enabled) { kms_request_opt_set_crypto_hooks ( opts, _sha256, _sha256_hmac, ctx_with_status); } } static bool is_kms (_kms_request_type_t kms_type) { return kms_type == MONGOCRYPT_KMS_KMIP_REGISTER || kms_type == MONGOCRYPT_KMS_KMIP_ACTIVATE || kms_type == MONGOCRYPT_KMS_KMIP_GET; } static void _init_common (mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _kms_request_type_t kms_type) { BSON_ASSERT_PARAM (kms); if (is_kms (kms_type)) { kms->parser = kms_kmip_response_parser_new (NULL /* reserved */); } else { kms->parser = kms_response_parser_new (); } kms->log = log; kms->status = mongocrypt_status_new (); kms->req_type = kms_type; _mongocrypt_buffer_init (&kms->result); } bool _mongocrypt_kms_ctx_init_aws_decrypt ( mongocrypt_kms_ctx_t *kms, _mongocrypt_opts_kms_providers_t *kms_providers, _mongocrypt_key_doc_t *key, _mongocrypt_log_t *log, _mongocrypt_crypto_t *crypto) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (key); BSON_ASSERT_PARAM (kms_providers); BSON_ASSERT_PARAM (crypto); kms_request_opt_t *opt; mongocrypt_status_t *status; ctx_with_status_t ctx_with_status; bool ret = false; _init_common (kms, log, MONGOCRYPT_KMS_AWS_DECRYPT); status = kms->status; ctx_with_status.ctx = crypto; ctx_with_status.status = mongocrypt_status_new (); if (!key->kek.kms_provider) { CLIENT_ERR ("no kms provider specified on key"); goto done; } if (MONGOCRYPT_KMS_PROVIDER_AWS != key->kek.kms_provider) { CLIENT_ERR ("expected aws kms provider"); goto done; } if (!key->kek.provider.aws.region) { CLIENT_ERR ("no key region provided"); goto done; } if (0 == (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS)) { CLIENT_ERR ("aws kms not configured"); goto done; } if (!kms_providers->aws.access_key_id) { CLIENT_ERR ("aws access key id not provided"); goto done; } if (!kms_providers->aws.secret_access_key) { CLIENT_ERR ("aws secret access key not provided"); goto done; } /* create the KMS request. */ opt = kms_request_opt_new (); BSON_ASSERT (opt); _set_kms_crypto_hooks (crypto, &ctx_with_status, opt); kms_request_opt_set_connection_close (opt, true); kms->req = kms_decrypt_request_new ( key->key_material.data, key->key_material.len, opt); kms_request_opt_destroy (opt); if (!kms_request_set_service (kms->req, "kms")) { CLIENT_ERR ("failed to set service: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } if (kms_providers->aws.session_token) { if (!kms_request_add_header_field (kms->req, "X-Amz-Security-Token", kms_providers->aws.session_token)) { CLIENT_ERR ("failed to set session token: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } } if (kms_request_get_error (kms->req)) { CLIENT_ERR ("error constructing KMS message: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } /* If an endpoint was set, override the default Host header. */ if (key->kek.provider.aws.endpoint) { if (!kms_request_add_header_field ( kms->req, "Host", key->kek.provider.aws.endpoint->host_and_port)) { CLIENT_ERR ("error constructing KMS message: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } } if (!kms_request_set_region (kms->req, key->kek.provider.aws.region)) { CLIENT_ERR ("failed to set region: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } if (!kms_request_set_access_key_id (kms->req, kms_providers->aws.access_key_id)) { CLIENT_ERR ("failed to set aws access key id: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } if (!kms_request_set_secret_key (kms->req, kms_providers->aws.secret_access_key)) { CLIENT_ERR ("failed to set aws secret access key: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } _mongocrypt_buffer_init (&kms->msg); kms->msg.data = (uint8_t *) kms_request_get_signed (kms->req); if (!kms->msg.data) { CLIENT_ERR ("failed to create KMS message: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } kms->msg.len = (uint32_t) strlen ((char *) kms->msg.data); kms->msg.owned = true; if (key->kek.provider.aws.endpoint) { kms->endpoint = bson_strdup (key->kek.provider.aws.endpoint->host_and_port); } else { /* construct the endpoint from AWS region. */ kms->endpoint = bson_strdup_printf ("kms.%s.amazonaws.com", key->kek.provider.aws.region); } _mongocrypt_apply_default_port (&kms->endpoint, DEFAULT_HTTPS_PORT); ret = true; done: mongocrypt_status_destroy (ctx_with_status.status); return ret; } bool _mongocrypt_kms_ctx_init_aws_encrypt ( mongocrypt_kms_ctx_t *kms, _mongocrypt_opts_kms_providers_t *kms_providers, _mongocrypt_ctx_opts_t *ctx_opts, _mongocrypt_buffer_t *plaintext_key_material, _mongocrypt_log_t *log, _mongocrypt_crypto_t *crypto) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (ctx_opts); BSON_ASSERT_PARAM (kms_providers); BSON_ASSERT_PARAM (crypto); BSON_ASSERT_PARAM (plaintext_key_material); kms_request_opt_t *opt; mongocrypt_status_t *status; ctx_with_status_t ctx_with_status; bool ret = false; _init_common (kms, log, MONGOCRYPT_KMS_AWS_ENCRYPT); status = kms->status; ctx_with_status.ctx = crypto; ctx_with_status.status = mongocrypt_status_new (); if (MONGOCRYPT_KMS_PROVIDER_AWS != ctx_opts->kek.kms_provider) { CLIENT_ERR ("expected aws kms provider"); goto done; } if (!ctx_opts->kek.provider.aws.region) { CLIENT_ERR ("no key region provided"); goto done; } if (!ctx_opts->kek.provider.aws.cmk) { CLIENT_ERR ("no aws cmk provided"); goto done; } if (0 == (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS)) { CLIENT_ERR ("aws kms not configured"); goto done; } if (!kms_providers->aws.access_key_id) { CLIENT_ERR ("aws access key id not provided"); goto done; } if (!kms_providers->aws.secret_access_key) { CLIENT_ERR ("aws secret access key not provided"); goto done; } /* create the KMS request. */ opt = kms_request_opt_new (); BSON_ASSERT (opt); _set_kms_crypto_hooks (crypto, &ctx_with_status, opt); kms_request_opt_set_connection_close (opt, true); kms->req = kms_encrypt_request_new (plaintext_key_material->data, plaintext_key_material->len, ctx_opts->kek.provider.aws.cmk, opt); kms_request_opt_destroy (opt); if (!kms_request_set_service (kms->req, "kms")) { CLIENT_ERR ("failed to set service: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } if (kms_providers->aws.session_token) { if (!kms_request_add_header_field (kms->req, "X-Amz-Security-Token", kms_providers->aws.session_token)) { CLIENT_ERR ("failed to set session token: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } } if (kms_request_get_error (kms->req)) { CLIENT_ERR ("error constructing KMS message: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } /* If an endpoint was set, override the default Host header. */ if (ctx_opts->kek.provider.aws.endpoint) { if (!kms_request_add_header_field ( kms->req, "Host", ctx_opts->kek.provider.aws.endpoint->host)) { CLIENT_ERR ("error constructing KMS message: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } } if (!kms_request_set_region (kms->req, ctx_opts->kek.provider.aws.region)) { CLIENT_ERR ("failed to set region: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } if (!kms_request_set_access_key_id (kms->req, kms_providers->aws.access_key_id)) { CLIENT_ERR ("failed to set aws access key id: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } if (!kms_request_set_secret_key (kms->req, kms_providers->aws.secret_access_key)) { CLIENT_ERR ("failed to set aws secret access key: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } _mongocrypt_buffer_init (&kms->msg); kms->msg.data = (uint8_t *) kms_request_get_signed (kms->req); if (!kms->msg.data) { CLIENT_ERR ("failed to create KMS message: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto done; } kms->msg.len = (uint32_t) strlen ((char *) kms->msg.data); kms->msg.owned = true; /* construct the endpoint */ if (ctx_opts->kek.provider.aws.endpoint) { kms->endpoint = bson_strdup (ctx_opts->kek.provider.aws.endpoint->host_and_port); } else { kms->endpoint = bson_strdup_printf ("kms.%s.amazonaws.com", ctx_opts->kek.provider.aws.region); } _mongocrypt_apply_default_port (&kms->endpoint, DEFAULT_HTTPS_PORT); ret = true; done: mongocrypt_status_destroy (ctx_with_status.status); return ret; } uint32_t mongocrypt_kms_ctx_bytes_needed (mongocrypt_kms_ctx_t *kms) { int want_bytes; if (!kms) { return 0; } /* TODO: an oddity of kms-message. After retrieving the result, it * resets the parser. */ if (!mongocrypt_status_ok (kms->status) || !_mongocrypt_buffer_empty (&kms->result)) { return 0; } want_bytes = kms_response_parser_wants_bytes (kms->parser, DEFAULT_MAX_KMS_BYTE_REQUEST); BSON_ASSERT (want_bytes >= 0); return (uint32_t) want_bytes; } static void _handle_non200_http_status (int http_status, const char *body, size_t body_len, mongocrypt_status_t *status) { BSON_ASSERT_PARAM (body); /* 1xx, 2xx, and 3xx HTTP status codes are not errors, but we only * support handling 200 response. */ if (http_status < 400) { CLIENT_ERR ("Unsupported HTTP code in KMS response. HTTP status=%d. " "Response body=\n%s", http_status, body); return; } /* Either empty body or body containing JSON with error message. */ if (body_len == 0) { CLIENT_ERR ("Error in KMS response. HTTP status=%d. Empty body.", http_status); return; } CLIENT_ERR ("Error in KMS response. HTTP status=%d. Response body=\n%s", http_status, body); } /* An AWS KMS context has received full response. Parse out the result or error. */ static bool _ctx_done_aws (mongocrypt_kms_ctx_t *kms, const char *json_field) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (json_field); kms_response_t *response = NULL; const char *body; bson_t body_bson = BSON_INITIALIZER; bool ret; bson_error_t bson_error; bson_iter_t iter; uint32_t b64_strlen; char *b64_str; int http_status; size_t body_len; int result_len; mongocrypt_status_t *status; status = kms->status; ret = false; /* Parse out the {en|de}crypted result. */ http_status = kms_response_parser_status (kms->parser); response = kms_response_parser_get_response (kms->parser); body = kms_response_get_body (response, &body_len); if (http_status != 200) { _handle_non200_http_status (http_status, body, body_len, status); goto fail; } /* If HTTP response succeeded (status 200) then body should contain JSON. */ bson_destroy (&body_bson); if (body_len > (size_t) SSIZE_MAX) { CLIENT_ERR ("Error parsing JSON in KMS response '%s'. " "Response body exceeds maximum supported length", bson_error.message); bson_init (&body_bson); goto fail; } if (!bson_init_from_json ( &body_bson, body, (ssize_t) body_len, &bson_error)) { CLIENT_ERR ("Error parsing JSON in KMS response '%s'. " "HTTP status=%d. Response body=\n%s", bson_error.message, http_status, body); bson_init (&body_bson); goto fail; } if (!bson_iter_init_find (&iter, &body_bson, json_field) || !BSON_ITER_HOLDS_UTF8 (&iter)) { CLIENT_ERR ( "KMS JSON response does not include field '%s'. HTTP status=%d. " "Response body=\n%s", json_field, http_status, body); goto fail; } b64_str = (char *) bson_iter_utf8 (&iter, &b64_strlen); BSON_ASSERT (b64_str); uint8_t *result_data = bson_malloc ((size_t) b64_strlen + 1u); BSON_ASSERT (result_data); result_len = kms_message_b64_pton (b64_str, result_data, b64_strlen); if (result_len < 0) { CLIENT_ERR ( "Failed to base64 decode response. HTTP status=%d. Response body=\n%s", http_status, body); bson_free (result_data); goto fail; } kms->result.data = result_data; kms->result.len = (uint32_t) result_len; kms->result.owned = true; ret = true; fail: bson_destroy (&body_bson); kms_response_destroy (response); return ret; } /* A Azure/GCP oauth KMS context has received full response. Parse out the * bearer token or error. */ static bool _ctx_done_oauth (mongocrypt_kms_ctx_t *kms) { BSON_ASSERT_PARAM (kms); kms_response_t *response = NULL; const char *body; bson_t *bson_body = NULL; bool ret; bson_error_t bson_error; bson_iter_t iter; int http_status; size_t body_len; mongocrypt_status_t *status; status = kms->status; ret = false; /* Parse out the oauth token result (or error). */ http_status = kms_response_parser_status (kms->parser); response = kms_response_parser_get_response (kms->parser); body = kms_response_get_body (response, &body_len); if (body_len == 0) { CLIENT_ERR ("Empty KMS response. HTTP status=%d", http_status); goto fail; } if (body_len > (size_t) SSIZE_MAX) { CLIENT_ERR ("Error parsing JSON in KMS response '%s'. " "Response body exceeds maximum supported length", bson_error.message); goto fail; } bson_body = bson_new_from_json ( (const uint8_t *) body, (ssize_t) body_len, &bson_error); if (!bson_body) { CLIENT_ERR ("Error parsing JSON in KMS response '%s'. " "HTTP status=%d. Response body=\n%s", bson_error.message, http_status, body); goto fail; } if (http_status != 200) { _handle_non200_http_status (http_status, body, body_len, status); goto fail; } if (!bson_iter_init_find (&iter, bson_body, "access_token") || !BSON_ITER_HOLDS_UTF8 (&iter)) { CLIENT_ERR ("Invalid KMS response. KMS JSON response does not include " "field 'access_token'. " "HTTP status=%d. Response body=\n%s", http_status, body); goto fail; } /* Store the full response, to include the expiration time. */ _mongocrypt_buffer_steal_from_bson (&kms->result, bson_body); bson_body = NULL; ret = true; fail: bson_destroy (bson_body); kms_response_destroy (response); return ret; } /* An Azure oauth KMS context has received full response. Parse out the bearer * token or error. */ static bool _ctx_done_azure_wrapkey_unwrapkey (mongocrypt_kms_ctx_t *kms) { BSON_ASSERT_PARAM (kms); kms_response_t *response = NULL; const char *body; bson_t *bson_body = NULL; bool ret; bson_error_t bson_error; bson_iter_t iter; int http_status; size_t body_len; mongocrypt_status_t *status; const char *b64url_data = NULL; uint32_t b64url_len; char *b64_data = NULL; uint32_t b64_len; int result_len; status = kms->status; ret = false; /* Parse out the oauth token result (or error). */ http_status = kms_response_parser_status (kms->parser); response = kms_response_parser_get_response (kms->parser); body = kms_response_get_body (response, &body_len); if (body_len == 0) { CLIENT_ERR ("Empty KMS response. HTTP status=%d", http_status); goto fail; } if (body_len > (size_t) SSIZE_MAX) { CLIENT_ERR ("Error parsing JSON in KMS response '%s'. " "Response body exceeds maximum supported length", bson_error.message); goto fail; } bson_body = bson_new_from_json ( (const uint8_t *) body, (ssize_t) body_len, &bson_error); if (!bson_body) { CLIENT_ERR ("Error parsing JSON in KMS response '%s'. " "HTTP status=%d. Response body=\n%s", bson_error.message, http_status, body); goto fail; } if (http_status != 200) { _handle_non200_http_status (http_status, body, body_len, status); goto fail; } if (!bson_iter_init_find (&iter, bson_body, "value") || !BSON_ITER_HOLDS_UTF8 (&iter)) { CLIENT_ERR ( "KMS JSON response does not include field 'value'. HTTP status=%d. " "Response body=\n%s", http_status, body); goto fail; } b64url_data = bson_iter_utf8 (&iter, &b64url_len); BSON_ASSERT (b64url_len <= UINT32_MAX - 4u); /* add four for padding. */ b64_len = b64url_len + 4; b64_data = bson_malloc0 (b64_len); if (kms_message_b64url_to_b64 (b64url_data, b64url_len, b64_data, b64_len) == -1) { CLIENT_ERR ("Error converting base64url to base64"); goto fail; } uint8_t *result_data = bson_malloc (b64_len); BSON_ASSERT (result_data); result_len = kms_message_b64_pton (b64_data, result_data, b64_len); if (result_len < 0) { CLIENT_ERR ( "Failed to base64 decode response. HTTP status=%d. Response body=\n%s", http_status, body); bson_free (result_data); goto fail; } kms->result.data = result_data; kms->result.len = (uint32_t) result_len; kms->result.owned = true; ret = true; fail: bson_destroy (bson_body); kms_response_destroy (response); bson_free (b64_data); return ret; } /* A GCP KMS context has received full response. Parse out the result or error. */ static bool _ctx_done_gcp (mongocrypt_kms_ctx_t *kms, const char *json_field) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (json_field); kms_response_t *response = NULL; const char *body; bson_t body_bson = BSON_INITIALIZER; bool ret; bson_error_t bson_error; bson_iter_t iter; size_t outlen; char *b64_str; int http_status; size_t body_len; mongocrypt_status_t *status; status = kms->status; ret = false; /* Parse out the {en|de}crypted result. */ http_status = kms_response_parser_status (kms->parser); response = kms_response_parser_get_response (kms->parser); body = kms_response_get_body (response, &body_len); if (http_status != 200) { _handle_non200_http_status (http_status, body, body_len, status); goto fail; } /* If HTTP response succeeded (status 200) then body should contain JSON. */ bson_destroy (&body_bson); if (body_len > (size_t) SSIZE_MAX) { CLIENT_ERR ("Error parsing JSON in KMS response '%s'. " "Response body exceeds maximum supported length", bson_error.message); bson_init (&body_bson); goto fail; } if (!bson_init_from_json ( &body_bson, body, (ssize_t) body_len, &bson_error)) { CLIENT_ERR ("Error parsing JSON in KMS response '%s'. " "HTTP status=%d. Response body=\n%s", bson_error.message, http_status, body); bson_init (&body_bson); goto fail; } if (!bson_iter_init_find (&iter, &body_bson, json_field) || !BSON_ITER_HOLDS_UTF8 (&iter)) { CLIENT_ERR ( "KMS JSON response does not include field '%s'. HTTP status=%d. " "Response body=\n%s", json_field, http_status, body); goto fail; } b64_str = (char *) bson_iter_utf8 (&iter, NULL); BSON_ASSERT (b64_str); kms->result.data = kms_message_b64_to_raw (b64_str, &outlen); BSON_ASSERT (outlen <= UINT32_MAX); kms->result.len = (uint32_t) outlen; kms->result.owned = true; ret = true; fail: bson_destroy (&body_bson); kms_response_destroy (response); return ret; } static bool _ctx_done_kmip_register (mongocrypt_kms_ctx_t *kms_ctx) { BSON_ASSERT_PARAM (kms_ctx); kms_response_t *res = NULL; mongocrypt_status_t *status = kms_ctx->status; bool ret = false; char *uid; res = kms_response_parser_get_response (kms_ctx->parser); if (!res) { CLIENT_ERR ("Error getting KMIP response: %s", kms_response_parser_error (kms_ctx->parser)); goto done; } uid = kms_kmip_response_get_unique_identifier (res); if (!uid) { CLIENT_ERR ( "Error getting UniqueIdentifer from KMIP Register response: %s", kms_response_get_error (res)); goto done; } if (!_mongocrypt_buffer_steal_from_string (&kms_ctx->result, uid)) { CLIENT_ERR ("Error storing KMS UniqueIdentifer result"); bson_free (uid); goto done; } ret = true; done: kms_response_destroy (res); return ret; } static bool _ctx_done_kmip_activate (mongocrypt_kms_ctx_t *kms_ctx) { BSON_ASSERT_PARAM (kms_ctx); return _ctx_done_kmip_register (kms_ctx); } static bool _ctx_done_kmip_get (mongocrypt_kms_ctx_t *kms_ctx) { BSON_ASSERT_PARAM (kms_ctx); kms_response_t *res = NULL; mongocrypt_status_t *status = kms_ctx->status; bool ret = false; uint8_t *secretdata; size_t secretdata_len; res = kms_response_parser_get_response (kms_ctx->parser); if (!res) { CLIENT_ERR ("Error getting KMIP response: %s", kms_response_parser_error (kms_ctx->parser)); goto done; } secretdata = kms_kmip_response_get_secretdata (res, &secretdata_len); if (!secretdata) { CLIENT_ERR ("Error getting SecretData from KMIP Get response: %s", kms_response_get_error (res)); goto done; } if (!_mongocrypt_buffer_steal_from_data_and_size ( &kms_ctx->result, secretdata, secretdata_len)) { CLIENT_ERR ("Error storing KMS SecretData result"); bson_free (secretdata); goto done; } ret = true; done: kms_response_destroy (res); return ret; } bool mongocrypt_kms_ctx_feed (mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes) { if (!kms) { return false; } mongocrypt_status_t *status = kms->status; if (!mongocrypt_status_ok (status)) { return false; } if (!bytes) { CLIENT_ERR ("argument 'bytes' is required"); return false; } if (0 == bytes->len) { CLIENT_ERR ("argument 'bytes' cannot be empty"); return false; } if (bytes->len > mongocrypt_kms_ctx_bytes_needed (kms)) { CLIENT_ERR ("KMS response fed too much data"); return false; } if (kms->log && kms->log->trace_enabled) { _mongocrypt_log (kms->log, MONGOCRYPT_LOG_LEVEL_TRACE, "%s (%s=\"%.*s\")", BSON_FUNC, "bytes", mongocrypt_binary_len (bytes), mongocrypt_binary_data (bytes)); } if (!kms_response_parser_feed (kms->parser, bytes->data, bytes->len)) { if (is_kms (kms->req_type)) { /* The KMIP response parser does not suport kms_response_parser_status. * Only report the error string. */ CLIENT_ERR ("KMS response parser error with error: '%s'", kms_response_parser_error (kms->parser)); } else { CLIENT_ERR ("KMS response parser error with status %d, error: '%s'", kms_response_parser_status (kms->parser), kms_response_parser_error (kms->parser)); } return false; } if (0 == mongocrypt_kms_ctx_bytes_needed (kms)) { switch (kms->req_type) { default: CLIENT_ERR ("Unknown request type"); return false; case MONGOCRYPT_KMS_AWS_ENCRYPT: return _ctx_done_aws (kms, "CiphertextBlob"); case MONGOCRYPT_KMS_AWS_DECRYPT: return _ctx_done_aws (kms, "Plaintext"); case MONGOCRYPT_KMS_AZURE_OAUTH: return _ctx_done_oauth (kms); case MONGOCRYPT_KMS_AZURE_WRAPKEY: return _ctx_done_azure_wrapkey_unwrapkey (kms); case MONGOCRYPT_KMS_AZURE_UNWRAPKEY: return _ctx_done_azure_wrapkey_unwrapkey (kms); case MONGOCRYPT_KMS_GCP_OAUTH: return _ctx_done_oauth (kms); case MONGOCRYPT_KMS_GCP_ENCRYPT: return _ctx_done_gcp (kms, "ciphertext"); case MONGOCRYPT_KMS_GCP_DECRYPT: return _ctx_done_gcp (kms, "plaintext"); case MONGOCRYPT_KMS_KMIP_REGISTER: return _ctx_done_kmip_register (kms); case MONGOCRYPT_KMS_KMIP_ACTIVATE: return _ctx_done_kmip_activate (kms); case MONGOCRYPT_KMS_KMIP_GET: return _ctx_done_kmip_get (kms); } } return true; } bool _mongocrypt_kms_ctx_result (mongocrypt_kms_ctx_t *kms, _mongocrypt_buffer_t *out) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (out); mongocrypt_status_t *status = kms->status; /* If we have no status, we were never initialized */ if (!status) { return false; } if (!mongocrypt_status_ok (status)) { return false; } if (mongocrypt_kms_ctx_bytes_needed (kms) > 0) { CLIENT_ERR ("KMS response unfinished"); return false; } _mongocrypt_buffer_init (out); out->data = kms->result.data; out->len = kms->result.len; return true; } bool mongocrypt_kms_ctx_status (mongocrypt_kms_ctx_t *kms, mongocrypt_status_t *status_out) { if (!kms) { return false; } if (!status_out) { mongocrypt_status_t *status = kms->status; CLIENT_ERR ("argument 'status' is required"); return false; } _mongocrypt_status_copy_to (kms->status, status_out); return mongocrypt_status_ok (status_out); } void _mongocrypt_kms_ctx_cleanup (mongocrypt_kms_ctx_t *kms) { if (!kms) { return; } if (kms->req) { kms_request_destroy (kms->req); } if (kms->parser) { kms_response_parser_destroy (kms->parser); } mongocrypt_status_destroy (kms->status); _mongocrypt_buffer_cleanup (&kms->msg); _mongocrypt_buffer_cleanup (&kms->result); bson_free (kms->endpoint); } bool mongocrypt_kms_ctx_message (mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *msg) { if (!kms) { return false; } if (!msg) { mongocrypt_status_t *status = kms->status; CLIENT_ERR ("argument 'msg' is required"); return false; } msg->data = kms->msg.data; msg->len = kms->msg.len; return true; } bool mongocrypt_kms_ctx_endpoint (mongocrypt_kms_ctx_t *kms, const char **endpoint) { if (!kms) { return false; } if (!endpoint) { mongocrypt_status_t *status = kms->status; CLIENT_ERR ("argument 'endpoint' is required"); return false; } *endpoint = kms->endpoint; return true; } bool _mongocrypt_kms_ctx_init_azure_auth ( mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _mongocrypt_opts_kms_providers_t *kms_providers, _mongocrypt_endpoint_t *key_vault_endpoint) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (kms_providers); kms_request_opt_t *opt = NULL; mongocrypt_status_t *status; _mongocrypt_endpoint_t *identity_platform_endpoint; char *scope = NULL; const char *hostname; char *request_string; bool ret = false; _init_common (kms, log, MONGOCRYPT_KMS_AZURE_OAUTH); status = kms->status; identity_platform_endpoint = kms_providers->azure.identity_platform_endpoint; if (identity_platform_endpoint) { kms->endpoint = bson_strdup (identity_platform_endpoint->host_and_port); hostname = identity_platform_endpoint->host; } else { kms->endpoint = bson_strdup ("login.microsoftonline.com"); hostname = "login.microsoftonline.com"; } _mongocrypt_apply_default_port (&kms->endpoint, DEFAULT_HTTPS_PORT); if (key_vault_endpoint) { /* Request a custom scope. It is URL encoded, like * https%3A%2F%2Fvault.azure.net%2F.default */ scope = bson_strdup_printf ( "%s%s%s", "https%3A%2F%2F", key_vault_endpoint->domain, "%2F.default"); } else { /* Default to commercial Azure endpoint. */ scope = bson_strdup ("https%3A%2F%2Fvault.azure.net%2F.default"); } opt = kms_request_opt_new (); BSON_ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_AZURE); kms->req = kms_azure_request_oauth_new (hostname, scope, kms_providers->azure.tenant_id, kms_providers->azure.client_id, kms_providers->azure.client_secret, opt); if (kms_request_get_error (kms->req)) { CLIENT_ERR ("error constructing KMS message: %s", kms_request_get_error (kms->req)); goto fail; } request_string = kms_request_to_string (kms->req); if (!request_string) { CLIENT_ERR ("error getting Azure OAuth KMS message: %s", kms_request_get_error (kms->req)); goto fail; } _mongocrypt_buffer_init (&kms->msg); kms->msg.data = (uint8_t *) request_string; kms->msg.len = (uint32_t) strlen (request_string); kms->msg.owned = true; ret = true; fail: bson_free (scope); kms_request_opt_destroy (opt); return ret; } bool _mongocrypt_kms_ctx_init_azure_wrapkey ( mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _mongocrypt_opts_kms_providers_t *kms_providers, struct __mongocrypt_ctx_opts_t *ctx_opts, const char *access_token, _mongocrypt_buffer_t *plaintext_key_material) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (ctx_opts); BSON_ASSERT_PARAM (plaintext_key_material); kms_request_opt_t *opt = NULL; mongocrypt_status_t *status; char *path_and_query = NULL; char *payload = NULL; const char *host; char *request_string; bool ret = false; _init_common (kms, log, MONGOCRYPT_KMS_AZURE_WRAPKEY); status = kms->status; BSON_ASSERT (ctx_opts->kek.provider.azure.key_vault_endpoint); kms->endpoint = bson_strdup ( ctx_opts->kek.provider.azure.key_vault_endpoint->host_and_port); _mongocrypt_apply_default_port (&kms->endpoint, DEFAULT_HTTPS_PORT); host = ctx_opts->kek.provider.azure.key_vault_endpoint->host; opt = kms_request_opt_new (); BSON_ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_AZURE); kms->req = kms_azure_request_wrapkey_new (host, access_token, ctx_opts->kek.provider.azure.key_name, ctx_opts->kek.provider.azure.key_version, plaintext_key_material->data, plaintext_key_material->len, opt); if (kms_request_get_error (kms->req)) { CLIENT_ERR ("error constructing KMS wrapkey message: %s", kms_request_get_error (kms->req)); goto fail; } request_string = kms_request_to_string (kms->req); if (!request_string) { CLIENT_ERR ("error getting Azure wrapkey KMS message: %s", kms_request_get_error (kms->req)); goto fail; } _mongocrypt_buffer_init (&kms->msg); kms->msg.data = (uint8_t *) request_string; kms->msg.len = (uint32_t) strlen (request_string); kms->msg.owned = true; ret = true; fail: kms_request_opt_destroy (opt); bson_free (path_and_query); bson_free (payload); return ret; } bool _mongocrypt_kms_ctx_init_azure_unwrapkey ( mongocrypt_kms_ctx_t *kms, _mongocrypt_opts_kms_providers_t *kms_providers, const char *access_token, _mongocrypt_key_doc_t *key, _mongocrypt_log_t *log) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (key); kms_request_opt_t *opt = NULL; mongocrypt_status_t *status; char *path_and_query = NULL; char *payload = NULL; const char *host; char *request_string; bool ret = false; _init_common (kms, log, MONGOCRYPT_KMS_AZURE_UNWRAPKEY); status = kms->status; BSON_ASSERT (key->kek.provider.azure.key_vault_endpoint); kms->endpoint = bson_strdup (key->kek.provider.azure.key_vault_endpoint->host_and_port); _mongocrypt_apply_default_port (&kms->endpoint, DEFAULT_HTTPS_PORT); host = key->kek.provider.azure.key_vault_endpoint->host; opt = kms_request_opt_new (); BSON_ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_AZURE); kms->req = kms_azure_request_unwrapkey_new (host, access_token, key->kek.provider.azure.key_name, key->kek.provider.azure.key_version, key->key_material.data, key->key_material.len, opt); if (kms_request_get_error (kms->req)) { CLIENT_ERR ("error constructing KMS unwrapkey message: %s", kms_request_get_error (kms->req)); goto fail; } request_string = kms_request_to_string (kms->req); if (!request_string) { CLIENT_ERR ("error getting Azure unwrapkey KMS message: %s", kms_request_get_error (kms->req)); goto fail; } _mongocrypt_buffer_init (&kms->msg); kms->msg.data = (uint8_t *) request_string; kms->msg.len = (uint32_t) strlen (request_string); kms->msg.owned = true; ret = true; fail: kms_request_opt_destroy (opt); bson_free (path_and_query); bson_free (payload); return ret; } #define RSAES_PKCS1_V1_5_SIGNATURE_LEN 256 /* This is the form of the callback that KMS message calls. */ static bool _sign_rsaes_pkcs1_v1_5_trampoline (void *ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out) { ctx_with_status_t *ctx_with_status; _mongocrypt_opts_t *crypt_opts; mongocrypt_binary_t private_key_bin; mongocrypt_binary_t input_bin; mongocrypt_binary_t output_bin; bool ret; BSON_ASSERT_PARAM (ctx); BSON_ASSERT_PARAM (input); BSON_ASSERT_PARAM (private_key); BSON_ASSERT_PARAM (signature_out); ctx_with_status = (ctx_with_status_t *) ctx; crypt_opts = (_mongocrypt_opts_t *) ctx_with_status->ctx; BSON_ASSERT (crypt_opts); private_key_bin.data = (uint8_t *) private_key; BSON_ASSERT (private_key_len <= UINT32_MAX); private_key_bin.len = (uint32_t) private_key_len; input_bin.data = (uint8_t *) input; BSON_ASSERT (input_len <= UINT32_MAX); input_bin.len = (uint32_t) input_len; output_bin.data = (uint8_t *) signature_out; output_bin.len = RSAES_PKCS1_V1_5_SIGNATURE_LEN; ret = crypt_opts->sign_rsaes_pkcs1_v1_5 (crypt_opts->sign_ctx, &private_key_bin, &input_bin, &output_bin, ctx_with_status->status); return ret; } bool _mongocrypt_kms_ctx_init_gcp_auth ( mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _mongocrypt_opts_t *crypt_opts, _mongocrypt_opts_kms_providers_t *kms_providers, _mongocrypt_endpoint_t *kms_endpoint) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (kms_providers); BSON_ASSERT_PARAM (crypt_opts); kms_request_opt_t *opt = NULL; mongocrypt_status_t *status; _mongocrypt_endpoint_t *auth_endpoint; char *scope = NULL; char *audience = NULL; const char *hostname; char *request_string; bool ret = false; ctx_with_status_t ctx_with_status; _init_common (kms, log, MONGOCRYPT_KMS_GCP_OAUTH); status = kms->status; ctx_with_status.ctx = crypt_opts; ctx_with_status.status = mongocrypt_status_new (); auth_endpoint = kms_providers->gcp.endpoint; if (auth_endpoint) { kms->endpoint = bson_strdup (auth_endpoint->host_and_port); hostname = auth_endpoint->host; audience = bson_strdup_printf ("https://%s/token", auth_endpoint->host); } else { kms->endpoint = bson_strdup ("oauth2.googleapis.com"); hostname = "oauth2.googleapis.com"; audience = bson_strdup_printf ("https://oauth2.googleapis.com/token"); } _mongocrypt_apply_default_port (&kms->endpoint, DEFAULT_HTTPS_PORT); if (kms_endpoint) { /* Request a custom scope. */ scope = bson_strdup_printf ("https://www.%s/auth/cloudkms", kms_endpoint->domain); } else { scope = bson_strdup ("https://www.googleapis.com/auth/cloudkms"); } opt = kms_request_opt_new (); BSON_ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_GCP); if (crypt_opts->sign_rsaes_pkcs1_v1_5) { kms_request_opt_set_crypto_hook_sign_rsaes_pkcs1_v1_5 ( opt, _sign_rsaes_pkcs1_v1_5_trampoline, &ctx_with_status); } kms->req = kms_gcp_request_oauth_new ( hostname, kms_providers->gcp.email, audience, scope, (const char *) kms_providers->gcp.private_key.data, kms_providers->gcp.private_key.len, opt); if (kms_request_get_error (kms->req)) { CLIENT_ERR ("error constructing KMS message: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto fail; } request_string = kms_request_to_string (kms->req); if (!request_string) { CLIENT_ERR ("error getting GCP OAuth KMS message: %s", kms_request_get_error (kms->req)); _mongocrypt_status_append (status, ctx_with_status.status); goto fail; } _mongocrypt_buffer_init (&kms->msg); kms->msg.data = (uint8_t *) request_string; kms->msg.len = (uint32_t) strlen (request_string); kms->msg.owned = true; ret = true; fail: bson_free (scope); bson_free (audience); kms_request_opt_destroy (opt); mongocrypt_status_destroy (ctx_with_status.status); return ret; } bool _mongocrypt_kms_ctx_init_gcp_encrypt ( mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _mongocrypt_opts_kms_providers_t *kms_providers, struct __mongocrypt_ctx_opts_t *ctx_opts, const char *access_token, _mongocrypt_buffer_t *plaintext_key_material) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (ctx_opts); BSON_ASSERT_PARAM (kms_providers); BSON_ASSERT_PARAM (access_token); BSON_ASSERT_PARAM (plaintext_key_material); kms_request_opt_t *opt = NULL; mongocrypt_status_t *status; char *path_and_query = NULL; char *payload = NULL; const char *hostname; char *request_string; bool ret = false; _init_common (kms, log, MONGOCRYPT_KMS_GCP_ENCRYPT); status = kms->status; if (ctx_opts->kek.provider.gcp.endpoint) { kms->endpoint = bson_strdup (ctx_opts->kek.provider.gcp.endpoint->host_and_port); hostname = ctx_opts->kek.provider.gcp.endpoint->host; } else { kms->endpoint = bson_strdup ("cloudkms.googleapis.com"); hostname = "cloudkms.googleapis.com"; } _mongocrypt_apply_default_port (&kms->endpoint, DEFAULT_HTTPS_PORT); opt = kms_request_opt_new (); BSON_ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_GCP); kms->req = kms_gcp_request_encrypt_new (hostname, access_token, ctx_opts->kek.provider.gcp.project_id, ctx_opts->kek.provider.gcp.location, ctx_opts->kek.provider.gcp.key_ring, ctx_opts->kek.provider.gcp.key_name, ctx_opts->kek.provider.gcp.key_version, plaintext_key_material->data, plaintext_key_material->len, opt); if (kms_request_get_error (kms->req)) { CLIENT_ERR ("error constructing GCP KMS encrypt message: %s", kms_request_get_error (kms->req)); goto fail; } request_string = kms_request_to_string (kms->req); if (!request_string) { CLIENT_ERR ("error getting GCP KMS encrypt KMS message: %s", kms_request_get_error (kms->req)); goto fail; } _mongocrypt_buffer_init (&kms->msg); kms->msg.data = (uint8_t *) request_string; kms->msg.len = (uint32_t) strlen (request_string); kms->msg.owned = true; ret = true; fail: kms_request_opt_destroy (opt); bson_free (path_and_query); bson_free (payload); return ret; } bool _mongocrypt_kms_ctx_init_gcp_decrypt ( mongocrypt_kms_ctx_t *kms, _mongocrypt_opts_kms_providers_t *kms_providers, const char *access_token, _mongocrypt_key_doc_t *key, _mongocrypt_log_t *log) { BSON_ASSERT_PARAM (kms); BSON_ASSERT_PARAM (kms_providers); BSON_ASSERT_PARAM (access_token); BSON_ASSERT_PARAM (key); kms_request_opt_t *opt = NULL; mongocrypt_status_t *status; char *path_and_query = NULL; char *payload = NULL; const char *hostname; char *request_string; bool ret = false; _init_common (kms, log, MONGOCRYPT_KMS_GCP_DECRYPT); status = kms->status; if (key->kek.provider.gcp.endpoint) { kms->endpoint = bson_strdup (key->kek.provider.gcp.endpoint->host_and_port); hostname = key->kek.provider.gcp.endpoint->host; } else { kms->endpoint = bson_strdup ("cloudkms.googleapis.com"); hostname = "cloudkms.googleapis.com"; } _mongocrypt_apply_default_port (&kms->endpoint, DEFAULT_HTTPS_PORT); opt = kms_request_opt_new (); BSON_ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_GCP); kms->req = kms_gcp_request_decrypt_new (hostname, access_token, key->kek.provider.gcp.project_id, key->kek.provider.gcp.location, key->kek.provider.gcp.key_ring, key->kek.provider.gcp.key_name, key->key_material.data, key->key_material.len, opt); if (kms_request_get_error (kms->req)) { CLIENT_ERR ("error constructing GCP KMS decrypt message: %s", kms_request_get_error (kms->req)); goto fail; } request_string = kms_request_to_string (kms->req); if (!request_string) { CLIENT_ERR ("error getting GCP KMS decrypt KMS message: %s", kms_request_get_error (kms->req)); goto fail; } _mongocrypt_buffer_init (&kms->msg); kms->msg.data = (uint8_t *) request_string; kms->msg.len = (uint32_t) strlen (request_string); kms->msg.owned = true; ret = true; fail: kms_request_opt_destroy (opt); bson_free (path_and_query); bson_free (payload); return ret; } bool _mongocrypt_kms_ctx_init_kmip_register (mongocrypt_kms_ctx_t *kms_ctx, const _mongocrypt_endpoint_t *endpoint, const uint8_t *secretdata, uint32_t secretdata_len, _mongocrypt_log_t *log) { BSON_ASSERT_PARAM (kms_ctx); BSON_ASSERT_PARAM (endpoint); BSON_ASSERT_PARAM (secretdata); mongocrypt_status_t *status; bool ret = false; const uint8_t *reqdata; size_t reqlen; _init_common (kms_ctx, log, MONGOCRYPT_KMS_KMIP_REGISTER); status = kms_ctx->status; kms_ctx->endpoint = bson_strdup (endpoint->host_and_port); _mongocrypt_apply_default_port (&kms_ctx->endpoint, DEFAULT_KMIP_PORT); kms_ctx->req = kms_kmip_request_register_secretdata_new ( NULL /* reserved */, secretdata, secretdata_len); if (kms_request_get_error (kms_ctx->req)) { CLIENT_ERR ("Error creating KMIP register request: %s", kms_request_get_error (kms_ctx->req)); goto done; } reqdata = kms_request_to_bytes (kms_ctx->req, &reqlen); if (!_mongocrypt_buffer_copy_from_data_and_size ( &kms_ctx->msg, reqdata, reqlen)) { CLIENT_ERR ("Error storing KMS request payload"); goto done; } ret = true; done: return ret; } bool _mongocrypt_kms_ctx_init_kmip_activate (mongocrypt_kms_ctx_t *kms_ctx, const _mongocrypt_endpoint_t *endpoint, const char *unique_identifier, _mongocrypt_log_t *log) { BSON_ASSERT_PARAM (kms_ctx); BSON_ASSERT_PARAM (endpoint); BSON_ASSERT_PARAM (unique_identifier); mongocrypt_status_t *status; bool ret = false; size_t reqlen; const uint8_t *reqdata; _init_common (kms_ctx, log, MONGOCRYPT_KMS_KMIP_ACTIVATE); status = kms_ctx->status; kms_ctx->endpoint = bson_strdup (endpoint->host_and_port); _mongocrypt_apply_default_port (&kms_ctx->endpoint, DEFAULT_KMIP_PORT); kms_ctx->req = kms_kmip_request_activate_new (NULL /* reserved */, unique_identifier); if (kms_request_get_error (kms_ctx->req)) { CLIENT_ERR ("Error creating KMIP activate request: %s", kms_request_get_error (kms_ctx->req)); goto done; } reqdata = kms_request_to_bytes (kms_ctx->req, &reqlen); if (!_mongocrypt_buffer_copy_from_data_and_size ( &kms_ctx->msg, reqdata, reqlen)) { CLIENT_ERR ("Error storing KMS request payload"); goto done; } ret = true; done: return ret; } bool _mongocrypt_kms_ctx_init_kmip_get (mongocrypt_kms_ctx_t *kms_ctx, const _mongocrypt_endpoint_t *endpoint, const char *unique_identifier, _mongocrypt_log_t *log) { BSON_ASSERT_PARAM (kms_ctx); BSON_ASSERT_PARAM (endpoint); BSON_ASSERT_PARAM (unique_identifier); mongocrypt_status_t *status; bool ret = false; size_t reqlen; const uint8_t *reqdata; _init_common (kms_ctx, log, MONGOCRYPT_KMS_KMIP_GET); status = kms_ctx->status; kms_ctx->endpoint = bson_strdup (endpoint->host_and_port); _mongocrypt_apply_default_port (&kms_ctx->endpoint, DEFAULT_KMIP_PORT); kms_ctx->req = kms_kmip_request_get_new (NULL /* reserved */, unique_identifier); if (kms_request_get_error (kms_ctx->req)) { CLIENT_ERR ("Error creating KMIP get request: %s", kms_request_get_error (kms_ctx->req)); goto done; } reqdata = kms_request_to_bytes (kms_ctx->req, &reqlen); if (!_mongocrypt_buffer_copy_from_data_and_size ( &kms_ctx->msg, reqdata, reqlen)) { CLIENT_ERR ("Error storing KMS request payload"); goto done; } ret = true; done: return ret; } static const char * set_and_ret (const char *what, uint32_t *len) { BSON_ASSERT_PARAM (what); if (len) { BSON_ASSERT (size_to_uint32 (strlen (what), len)); } return what; } const char * mongocrypt_kms_ctx_get_kms_provider (mongocrypt_kms_ctx_t *kms, uint32_t *len) { BSON_ASSERT_PARAM (kms); /* len is checked in set_and_ret () before it is used */ switch (kms->req_type) { default: BSON_ASSERT (false && "unknown KMS request type"); case MONGOCRYPT_KMS_AWS_ENCRYPT: case MONGOCRYPT_KMS_AWS_DECRYPT: return set_and_ret ("aws", len); case MONGOCRYPT_KMS_AZURE_OAUTH: case MONGOCRYPT_KMS_AZURE_WRAPKEY: case MONGOCRYPT_KMS_AZURE_UNWRAPKEY: return set_and_ret ("azure", len); case MONGOCRYPT_KMS_GCP_OAUTH: case MONGOCRYPT_KMS_GCP_ENCRYPT: case MONGOCRYPT_KMS_GCP_DECRYPT: return set_and_ret ("gcp", len); case MONGOCRYPT_KMS_KMIP_REGISTER: case MONGOCRYPT_KMS_KMIP_ACTIVATE: case MONGOCRYPT_KMS_KMIP_GET: return set_and_ret ("kmip", len); } }