|
| 1 | +/** |
| 2 | + * Copyright 2022 MongoDB, Inc. |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +#include "./mcd-azure.h" |
| 18 | + |
| 19 | +#include "mongoc-util-private.h" |
| 20 | + |
| 21 | +#define AZURE_API_VERSION "2018-02-01" |
| 22 | + |
| 23 | +static const char *const DEFAULT_METADATA_PATH = |
| 24 | + "/metadata/identity/oauth2/" |
| 25 | + "token?api-version=" AZURE_API_VERSION |
| 26 | + "&resource=https%3A%2F%2Fvault.azure.net"; |
| 27 | + |
| 28 | +void |
| 29 | +mcd_azure_imds_request_init (mcd_azure_imds_request *req) |
| 30 | +{ |
| 31 | + BSON_ASSERT_PARAM (req); |
| 32 | + _mongoc_http_request_init (&req->req); |
| 33 | + // The HTTP host of the IMDS server |
| 34 | + req->req.host = "169.254.169.254"; |
| 35 | + // No body |
| 36 | + req->req.body = ""; |
| 37 | + // We GET |
| 38 | + req->req.method = "GET"; |
| 39 | + // 'Metadata: true' is required |
| 40 | + req->req.extra_headers = "Metadata: true\r\n" |
| 41 | + "Accept: application/json\r\n"; |
| 42 | + // The default path is suitable. In the future, we may want to add query |
| 43 | + // parameters to disambiguate a managed identity. |
| 44 | + req->req.path = bson_strdup (DEFAULT_METADATA_PATH); |
| 45 | +} |
| 46 | + |
| 47 | +void |
| 48 | +mcd_azure_imds_request_destroy (mcd_azure_imds_request *req) |
| 49 | +{ |
| 50 | + BSON_ASSERT_PARAM (req); |
| 51 | + bson_free ((void *) req->req.path); |
| 52 | + *req = (mcd_azure_imds_request){0}; |
| 53 | +} |
| 54 | + |
| 55 | +bool |
| 56 | +mcd_azure_access_token_try_init_from_json_str (mcd_azure_access_token *out, |
| 57 | + const char *json, |
| 58 | + int len, |
| 59 | + bson_error_t *error) |
| 60 | +{ |
| 61 | + BSON_ASSERT_PARAM (out); |
| 62 | + BSON_ASSERT_PARAM (json); |
| 63 | + bool okay = false; |
| 64 | + |
| 65 | + if (len < 0) { |
| 66 | + // Detect from a null-terminated string |
| 67 | + len = strlen (json); |
| 68 | + } |
| 69 | + |
| 70 | + // Zero the output |
| 71 | + *out = (mcd_azure_access_token){0}; |
| 72 | + |
| 73 | + // Parse the JSON data |
| 74 | + bson_t bson; |
| 75 | + if (!bson_init_from_json (&bson, json, len, error)) { |
| 76 | + return false; |
| 77 | + } |
| 78 | + |
| 79 | + bson_iter_t iter; |
| 80 | + // access_token |
| 81 | + bool found = bson_iter_init_find (&iter, &bson, "access_token"); |
| 82 | + const char *const access_token = |
| 83 | + !found ? NULL : bson_iter_utf8 (&iter, NULL); |
| 84 | + // resource |
| 85 | + found = bson_iter_init_find (&iter, &bson, "resource"); |
| 86 | + const char *const resource = !found ? NULL : bson_iter_utf8 (&iter, NULL); |
| 87 | + // token_type |
| 88 | + found = bson_iter_init_find (&iter, &bson, "token_type"); |
| 89 | + const char *const token_type = !found ? NULL : bson_iter_utf8 (&iter, NULL); |
| 90 | + // expires_in |
| 91 | + found = bson_iter_init_find (&iter, &bson, "expires_in"); |
| 92 | + uint32_t expires_in_len = 0; |
| 93 | + const char *const expires_in_str = |
| 94 | + !found ? NULL : bson_iter_utf8 (&iter, &expires_in_len); |
| 95 | + |
| 96 | + if (!(access_token && resource && token_type && expires_in_str)) { |
| 97 | + bson_set_error ( |
| 98 | + error, |
| 99 | + MONGOC_ERROR_PROTOCOL_ERROR, |
| 100 | + 64, |
| 101 | + "One or more required JSON properties are missing/invalid: data: %.*s", |
| 102 | + len, |
| 103 | + json); |
| 104 | + } else { |
| 105 | + // Set the output, duplicate each string |
| 106 | + *out = (mcd_azure_access_token){ |
| 107 | + .access_token = bson_strdup (access_token), |
| 108 | + .resource = bson_strdup (resource), |
| 109 | + .token_type = bson_strdup (token_type), |
| 110 | + }; |
| 111 | + // "expires_in" encodes the number of seconds since the issue time for |
| 112 | + // which the token will be valid. strtoll() will saturate on range errors |
| 113 | + // and return zero on parse errors. |
| 114 | + char *parse_end; |
| 115 | + long long s = strtoll (expires_in_str, &parse_end, 0); |
| 116 | + if (parse_end != expires_in_str + expires_in_len) { |
| 117 | + // Did not parse the entire string. Bad |
| 118 | + bson_set_error ( |
| 119 | + error, |
| 120 | + MONGOC_ERROR_PROTOCOL, |
| 121 | + 65, |
| 122 | + "Invalid 'expires_in' string \"%.*s\" from IMDS server", |
| 123 | + expires_in_len, |
| 124 | + expires_in_str); |
| 125 | + } else { |
| 126 | + out->expires_in = mcd_seconds (s); |
| 127 | + okay = true; |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + bson_destroy (&bson); |
| 132 | + return okay; |
| 133 | +} |
| 134 | + |
| 135 | + |
| 136 | +void |
| 137 | +mcd_azure_access_token_destroy (mcd_azure_access_token *c) |
| 138 | +{ |
| 139 | + bson_free (c->access_token); |
| 140 | + bson_free (c->resource); |
| 141 | + bson_free (c->token_type); |
| 142 | + c->access_token = NULL; |
| 143 | + c->resource = NULL; |
| 144 | + c->token_type = NULL; |
| 145 | +} |
0 commit comments