Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : #include "src/core/security/credentials.h"
35 :
36 : #include <string.h>
37 :
38 : #include <grpc/support/alloc.h>
39 : #include <grpc/support/log.h>
40 : #include <grpc/support/sync.h>
41 :
42 : #include "src/core/httpcli/httpcli.h"
43 : #include "src/core/support/env.h"
44 : #include "src/core/support/file.h"
45 : #include "src/core/surface/api_trace.h"
46 :
47 : /* -- Constants. -- */
48 :
49 : #define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal"
50 :
51 : /* -- Default credentials. -- */
52 :
53 : static grpc_credentials *default_credentials = NULL;
54 : static int compute_engine_detection_done = 0;
55 : static gpr_mu g_mu;
56 : static gpr_once g_once = GPR_ONCE_INIT;
57 :
58 1 : static void init_default_credentials(void) { gpr_mu_init(&g_mu); }
59 :
60 : typedef struct {
61 : grpc_pollset pollset;
62 : int is_done;
63 : int success;
64 : } compute_engine_detector;
65 :
66 0 : static void on_compute_engine_detection_http_response(
67 : grpc_exec_ctx *exec_ctx, void *user_data,
68 : const grpc_httpcli_response *response) {
69 0 : compute_engine_detector *detector = (compute_engine_detector *)user_data;
70 0 : if (response != NULL && response->status == 200 && response->hdr_count > 0) {
71 : /* Internet providers can return a generic response to all requests, so
72 : it is necessary to check that metadata header is present also. */
73 : size_t i;
74 0 : for (i = 0; i < response->hdr_count; i++) {
75 0 : grpc_httpcli_header *header = &response->hdrs[i];
76 0 : if (strcmp(header->key, "Metadata-Flavor") == 0 &&
77 0 : strcmp(header->value, "Google") == 0) {
78 0 : detector->success = 1;
79 0 : break;
80 : }
81 : }
82 : }
83 0 : gpr_mu_lock(GRPC_POLLSET_MU(&detector->pollset));
84 0 : detector->is_done = 1;
85 0 : grpc_pollset_kick(&detector->pollset, NULL);
86 0 : gpr_mu_unlock(GRPC_POLLSET_MU(&detector->pollset));
87 0 : }
88 :
89 0 : static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, int s) {
90 0 : grpc_pollset_destroy(p);
91 0 : }
92 :
93 0 : static int is_stack_running_on_compute_engine(void) {
94 : compute_engine_detector detector;
95 : grpc_httpcli_request request;
96 : grpc_httpcli_context context;
97 0 : grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
98 : grpc_closure destroy_closure;
99 :
100 : /* The http call is local. If it takes more than one sec, it is for sure not
101 : on compute engine. */
102 0 : gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN);
103 :
104 0 : grpc_pollset_init(&detector.pollset);
105 0 : detector.is_done = 0;
106 0 : detector.success = 0;
107 :
108 0 : memset(&request, 0, sizeof(grpc_httpcli_request));
109 0 : request.host = GRPC_COMPUTE_ENGINE_DETECTION_HOST;
110 0 : request.path = "/";
111 :
112 0 : grpc_httpcli_context_init(&context);
113 :
114 0 : grpc_httpcli_get(
115 : &exec_ctx, &context, &detector.pollset, &request,
116 : gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay),
117 : on_compute_engine_detection_http_response, &detector);
118 :
119 0 : grpc_exec_ctx_finish(&exec_ctx);
120 :
121 : /* Block until we get the response. This is not ideal but this should only be
122 : called once for the lifetime of the process by the default credentials. */
123 0 : gpr_mu_lock(GRPC_POLLSET_MU(&detector.pollset));
124 0 : while (!detector.is_done) {
125 : grpc_pollset_worker worker;
126 0 : grpc_pollset_work(&exec_ctx, &detector.pollset, &worker,
127 : gpr_now(GPR_CLOCK_MONOTONIC),
128 : gpr_inf_future(GPR_CLOCK_MONOTONIC));
129 : }
130 0 : gpr_mu_unlock(GRPC_POLLSET_MU(&detector.pollset));
131 :
132 0 : grpc_httpcli_context_destroy(&context);
133 0 : grpc_closure_init(&destroy_closure, destroy_pollset, &detector.pollset);
134 0 : grpc_pollset_shutdown(&exec_ctx, &detector.pollset, &destroy_closure);
135 0 : grpc_exec_ctx_finish(&exec_ctx);
136 :
137 0 : return detector.success;
138 : }
139 :
140 : /* Takes ownership of creds_path if not NULL. */
141 2 : static grpc_credentials *create_default_creds_from_path(char *creds_path) {
142 2 : grpc_json *json = NULL;
143 : grpc_auth_json_key key;
144 : grpc_auth_refresh_token token;
145 2 : grpc_credentials *result = NULL;
146 2 : gpr_slice creds_data = gpr_empty_slice();
147 2 : int file_ok = 0;
148 2 : if (creds_path == NULL) goto end;
149 2 : creds_data = gpr_load_file(creds_path, 0, &file_ok);
150 2 : if (!file_ok) goto end;
151 4 : json = grpc_json_parse_string_with_len(
152 4 : (char *)GPR_SLICE_START_PTR(creds_data), GPR_SLICE_LENGTH(creds_data));
153 2 : if (json == NULL) goto end;
154 :
155 : /* First, try an auth json key. */
156 2 : key = grpc_auth_json_key_create_from_json(json);
157 2 : if (grpc_auth_json_key_is_valid(&key)) {
158 1 : result =
159 : grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
160 : key, grpc_max_auth_token_lifetime);
161 1 : goto end;
162 : }
163 :
164 : /* Then try a refresh token if the auth json key was invalid. */
165 1 : token = grpc_auth_refresh_token_create_from_json(json);
166 1 : if (grpc_auth_refresh_token_is_valid(&token)) {
167 1 : result =
168 : grpc_refresh_token_credentials_create_from_auth_refresh_token(token);
169 1 : goto end;
170 : }
171 :
172 : end:
173 2 : if (creds_path != NULL) gpr_free(creds_path);
174 2 : gpr_slice_unref(creds_data);
175 2 : if (json != NULL) grpc_json_destroy(json);
176 2 : return result;
177 : }
178 :
179 2 : grpc_credentials *grpc_google_default_credentials_create(void) {
180 2 : grpc_credentials *result = NULL;
181 2 : int serving_cached_credentials = 0;
182 :
183 2 : GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
184 :
185 2 : gpr_once_init(&g_once, init_default_credentials);
186 :
187 2 : gpr_mu_lock(&g_mu);
188 :
189 2 : if (default_credentials != NULL) {
190 0 : result = grpc_credentials_ref(default_credentials);
191 0 : serving_cached_credentials = 1;
192 0 : goto end;
193 : }
194 :
195 : /* First, try the environment variable. */
196 2 : result = create_default_creds_from_path(
197 : gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR));
198 2 : if (result != NULL) goto end;
199 :
200 : /* Then the well-known file. */
201 0 : result = create_default_creds_from_path(
202 : grpc_get_well_known_google_credentials_file_path());
203 0 : if (result != NULL) goto end;
204 :
205 : /* At last try to see if we're on compute engine (do the detection only once
206 : since it requires a network test). */
207 0 : if (!compute_engine_detection_done) {
208 0 : int need_compute_engine_creds = is_stack_running_on_compute_engine();
209 0 : compute_engine_detection_done = 1;
210 0 : if (need_compute_engine_creds) {
211 0 : result = grpc_google_compute_engine_credentials_create(NULL);
212 : }
213 : }
214 :
215 : end:
216 2 : if (!serving_cached_credentials && result != NULL) {
217 : /* Blend with default ssl credentials and add a global reference so that it
218 : can be cached and re-served. */
219 2 : grpc_credentials *ssl_creds = grpc_ssl_credentials_create(NULL, NULL, NULL);
220 2 : default_credentials = grpc_credentials_ref(
221 : grpc_composite_credentials_create(ssl_creds, result, NULL));
222 2 : GPR_ASSERT(default_credentials != NULL);
223 2 : grpc_credentials_unref(ssl_creds);
224 2 : grpc_credentials_unref(result);
225 2 : result = default_credentials;
226 : }
227 2 : gpr_mu_unlock(&g_mu);
228 2 : return result;
229 : }
230 :
231 2 : void grpc_flush_cached_google_default_credentials(void) {
232 2 : gpr_once_init(&g_once, init_default_credentials);
233 2 : gpr_mu_lock(&g_mu);
234 2 : if (default_credentials != NULL) {
235 1 : grpc_credentials_unref(default_credentials);
236 1 : default_credentials = NULL;
237 : }
238 2 : gpr_mu_unlock(&g_mu);
239 2 : }
|