27
27
#include "mongoc-host-list-private.h"
28
28
#include "mongoc-stream-private.h"
29
29
#include "mongoc-ssl-private.h"
30
+ #include "mongoc-cluster-aws-private.h"
30
31
#include "mongoc-util-private.h"
31
32
32
33
struct __mongoc_crypt_t {
@@ -35,6 +36,10 @@ struct __mongoc_crypt_t {
35
36
mongoc_ssl_opt_t aws_tls_opt ;
36
37
mongoc_ssl_opt_t azure_tls_opt ;
37
38
mongoc_ssl_opt_t gcp_tls_opt ;
39
+ /// The kmsProviders that were provided by the user when encryption was
40
+ /// initiated. We need to remember this in case we need to load on-demand
41
+ /// credentials.
42
+ bson_t kms_providers ;
38
43
mc_kms_credentials_callback creds_cb ;
39
44
};
40
45
@@ -576,37 +581,144 @@ _state_need_kms (_state_machine_t *state_machine, bson_error_t *error)
576
581
#undef BUFFER_SIZE
577
582
}
578
583
584
+ /**
585
+ * @brief Determine whether the given kmsProviders has an empty 'aws'
586
+ * subdocument
587
+ *
588
+ * @param kms_providers The user-provided kmsProviders
589
+ * @param error Output parameter for possible errors.
590
+ * @return true If 'aws' is present and an empty subdocument
591
+ * @return false Otherwise or on error
592
+ */
593
+ static bool
594
+ _needs_on_demand_aws_kms (bson_t const * kms_providers , bson_error_t * error )
595
+ {
596
+ bson_iter_t iter ;
597
+ if (!bson_iter_init_find (& iter , kms_providers , "aws" )) {
598
+ // No "aws" subdocument
599
+ return false;
600
+ }
601
+
602
+ if (!BSON_ITER_HOLDS_DOCUMENT (& iter )) {
603
+ // "aws" is not a document? Should be validated by libmongocrypt
604
+ return false;
605
+ }
606
+
607
+ const uint8_t * dataptr ;
608
+ uint32_t datalen ;
609
+ bson_iter_document (& iter , & datalen , & dataptr );
610
+ bson_t subdoc ;
611
+ if (!bson_init_static (& subdoc , dataptr , datalen )) {
612
+ // Invalid "aws" document? Should be validated by libmongocrypt
613
+ return false;
614
+ }
615
+
616
+ if (bson_empty (& subdoc )) {
617
+ // "aws" is present and is an empty subdocument, which means that the user
618
+ // requests that the AWS credentials be loaded on-demand from the
619
+ // environment.
620
+ return true;
621
+ } else {
622
+ // "aws" is present and is non-empty, which means that the user has
623
+ // already provided credentials for AWS.
624
+ return false;
625
+ }
626
+ }
627
+
628
+ /**
629
+ * @brief Attempt to load AWS credentials from the environment and insert them
630
+ * into the given kmsProviders bson document on the "aws" property.
631
+ *
632
+ * @param out A kmsProviders object to update
633
+ * @param error An error-out parameter
634
+ * @return true If there was no error and we successfully loaded credentials.
635
+ * @return false If there was an error while updating the BSON data or obtaining
636
+ * credentials.
637
+ */
638
+ static bool
639
+ _try_add_aws_from_env (bson_t * out , bson_error_t * error )
640
+ {
641
+ // Attempt to obtain AWS credentials from the environment.
642
+ _mongoc_aws_credentials_t creds ;
643
+ if (!_mongoc_aws_credentials_obtain (NULL , & creds , error )) {
644
+ // Error while obtaining credentials
645
+ return false;
646
+ }
647
+
648
+ // Build the new "aws" subdoc
649
+ bson_t aws ;
650
+ bool okay =
651
+ BSON_APPEND_DOCUMENT_BEGIN (out , "aws" , & aws )
652
+ // Add the accessKeyId and the secretAccessKey
653
+ && BSON_APPEND_UTF8 (& aws , "accessKeyId" , creds .access_key_id ) //
654
+ && BSON_APPEND_UTF8 (& aws , "secretAccessKey" , creds .secret_access_key ) //
655
+ // Add the sessionToken, if we got one:
656
+ && (!creds .session_token ||
657
+ BSON_APPEND_UTF8 (& aws , "sessionToken" , creds .session_token )) //
658
+ // Finish the document
659
+ && bson_append_document_end (out , & aws );
660
+ BSON_ASSERT (okay && "Failed to build aws credentials document" );
661
+ // Good!
662
+ return true;
663
+ }
664
+
579
665
static bool
580
666
_state_need_kms_credentials (_state_machine_t * sm , bson_error_t * error )
581
667
{
582
668
bson_t creds = BSON_INITIALIZER ;
583
- BSON_ASSERT (sm -> crypt -> creds_cb .fn );
584
669
const bson_t empty = BSON_INITIALIZER ;
670
+ bool okay = false;
671
+
672
+ if (sm -> crypt -> creds_cb .fn ) {
673
+ // We have a user-provided credentials callback. Try it.
674
+ if (!sm -> crypt -> creds_cb .fn (
675
+ sm -> crypt -> creds_cb .userdata , & empty , & creds , error )) {
676
+ // User-provided callback indicated failure
677
+ if (!error -> code ) {
678
+ // The callback did not set an error, so we'll provide a default
679
+ // one.
680
+ bson_set_error (error ,
681
+ MONGOC_ERROR_CLIENT_SIDE_ENCRYPTION ,
682
+ MONGOC_ERROR_CLIENT_INVALID_ENCRYPTION_ARG ,
683
+ "The user-provided callback for on-demand KMS "
684
+ "credentials failed." );
685
+ }
686
+ goto fail ;
687
+ }
688
+ // The user's callback reported success
689
+ }
585
690
586
- if (!sm -> crypt -> creds_cb .fn (
587
- sm -> crypt -> creds_cb .userdata , & empty , & creds , error )) {
588
- // The callback reports that it has failed
589
- if (!error -> code ) {
590
- bson_set_error (error ,
591
- MONGOC_ERROR_CLIENT_SIDE_ENCRYPTION ,
592
- MONGOC_ERROR_CLIENT_INVALID_ENCRYPTION_ARG ,
593
- "Unknown error from user-provided callback for "
594
- "on-demand KMS credentials" );
691
+ bson_iter_t iter ;
692
+ const bool callback_provided_aws =
693
+ bson_iter_init_find (& iter , & creds , "aws" );
694
+
695
+ if (!callback_provided_aws &&
696
+ _needs_on_demand_aws_kms (& sm -> crypt -> kms_providers , error )) {
697
+ // The original kmsProviders had an empty "aws" property, and the
698
+ // user-provided callback did not fill in a new "aws" property for us.
699
+ // Attempt instead to load the AWS credentials from the environment:
700
+ if (!_try_add_aws_from_env (& creds , error )) {
701
+ // Error while trying to add AWS credentials
702
+ goto fail ;
595
703
}
596
- return false;
597
704
}
598
705
599
- mongocrypt_binary_t * data = mongocrypt_binary_new_from_data (
706
+ // Now actually send that data to libmongocrypt
707
+ mongocrypt_binary_t * const def = mongocrypt_binary_new_from_data (
600
708
(uint8_t * ) bson_get_data (& creds ), creds .len );
601
- bool okay = mongocrypt_ctx_provide_kms_providers (sm -> ctx , data );
709
+ okay = mongocrypt_ctx_provide_kms_providers (sm -> ctx , def );
602
710
if (!okay ) {
603
711
_ctx_check_error (sm -> ctx , error , true);
604
712
}
605
- mongocrypt_binary_destroy (data );
713
+ mongocrypt_binary_destroy (def );
714
+
715
+ fail :
606
716
bson_destroy (& creds );
717
+
607
718
return okay ;
608
719
}
609
720
721
+
610
722
static bool
611
723
_state_ready (_state_machine_t * state_machine ,
612
724
bson_t * result ,
@@ -965,6 +1077,10 @@ _mongoc_crypt_new (const bson_t *kms_providers,
965
1077
crypt = bson_malloc0 (sizeof (* crypt ));
966
1078
crypt -> handle = mongocrypt_new ();
967
1079
1080
+ // Stash away a copy of the user's kmsProviders in case we need to lazily
1081
+ // load credentials.
1082
+ bson_copy_to (kms_providers , & crypt -> kms_providers );
1083
+
968
1084
if (!_parse_all_tls_opts (crypt , tls_opts , error )) {
969
1085
goto fail ;
970
1086
}
@@ -979,12 +1095,6 @@ _mongoc_crypt_new (const bson_t *kms_providers,
979
1095
goto fail ;
980
1096
}
981
1097
982
- if (creds_cb .fn ) {
983
- // The user has provided a callback to lazily obtain KMS credentials. We
984
- // need to opt-in to the libmongocrypt feature.
985
- mongocrypt_setopt_use_need_kms_credentials_state (crypt -> handle );
986
- }
987
-
988
1098
if (schema_map ) {
989
1099
schema_map_bin = mongocrypt_binary_new_from_data (
990
1100
(uint8_t * ) bson_get_data (schema_map ), schema_map -> len );
@@ -1028,6 +1138,9 @@ _mongoc_crypt_new (const bson_t *kms_providers,
1028
1138
}
1029
1139
}
1030
1140
1141
+ // Enable the NEEDS_CREDENTIALS state for on-demand credential loading
1142
+ mongocrypt_setopt_use_need_kms_credentials_state (crypt -> handle );
1143
+
1031
1144
if (!mongocrypt_init (crypt -> handle )) {
1032
1145
_crypt_check_error (crypt -> handle , error , true);
1033
1146
goto fail ;
@@ -1082,6 +1195,7 @@ _mongoc_crypt_destroy (_mongoc_crypt_t *crypt)
1082
1195
_mongoc_ssl_opts_cleanup (& crypt -> aws_tls_opt , true /* free_internal */ );
1083
1196
_mongoc_ssl_opts_cleanup (& crypt -> azure_tls_opt , true /* free_internal */ );
1084
1197
_mongoc_ssl_opts_cleanup (& crypt -> gcp_tls_opt , true /* free_internal */ );
1198
+ bson_destroy (& crypt -> kms_providers );
1085
1199
bson_free (crypt );
1086
1200
}
1087
1201
0 commit comments