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_channel_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 1 : static void on_compute_engine_detection_http_response(
67 : grpc_exec_ctx *exec_ctx, void *user_data,
68 : const grpc_httpcli_response *response) {
69 1 : compute_engine_detector *detector = (compute_engine_detector *)user_data;
70 1 : 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 1 : for (i = 0; i < response->hdr_count; i++) {
75 1 : grpc_httpcli_header *header = &response->hdrs[i];
76 2 : if (strcmp(header->key, "Metadata-Flavor") == 0 &&
77 1 : strcmp(header->value, "Google") == 0) {
78 1 : detector->success = 1;
79 1 : break;
80 : }
81 : }
82 : }
83 1 : gpr_mu_lock(GRPC_POLLSET_MU(&detector->pollset));
84 1 : detector->is_done = 1;
85 1 : grpc_pollset_kick(&detector->pollset, NULL);
86 1 : gpr_mu_unlock(GRPC_POLLSET_MU(&detector->pollset));
87 1 : }
88 :
89 1 : static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, int s) {
90 1 : grpc_pollset_destroy(p);
91 1 : }
92 :
93 1 : 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 1 : 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 1 : gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN);
103 :
104 1 : grpc_pollset_init(&detector.pollset);
105 1 : detector.is_done = 0;
106 1 : detector.success = 0;
107 :
108 1 : memset(&request, 0, sizeof(grpc_httpcli_request));
109 1 : request.host = GRPC_COMPUTE_ENGINE_DETECTION_HOST;
110 1 : request.path = "/";
111 :
112 1 : grpc_httpcli_context_init(&context);
113 :
114 1 : 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 1 : 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 1 : gpr_mu_lock(GRPC_POLLSET_MU(&detector.pollset));
124 2 : 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 1 : gpr_mu_unlock(GRPC_POLLSET_MU(&detector.pollset));
131 :
132 1 : grpc_httpcli_context_destroy(&context);
133 1 : grpc_closure_init(&destroy_closure, destroy_pollset, &detector.pollset);
134 1 : grpc_pollset_shutdown(&exec_ctx, &detector.pollset, &destroy_closure);
135 1 : grpc_exec_ctx_finish(&exec_ctx);
136 :
137 1 : return detector.success;
138 : }
139 :
140 : /* Takes ownership of creds_path if not NULL. */
141 4 : static grpc_call_credentials *create_default_creds_from_path(char *creds_path) {
142 4 : grpc_json *json = NULL;
143 : grpc_auth_json_key key;
144 : grpc_auth_refresh_token token;
145 4 : grpc_call_credentials *result = NULL;
146 4 : gpr_slice creds_data = gpr_empty_slice();
147 4 : int file_ok = 0;
148 4 : if (creds_path == NULL) goto end;
149 3 : creds_data = gpr_load_file(creds_path, 0, &file_ok);
150 3 : 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 4 : if (creds_path != NULL) gpr_free(creds_path);
174 4 : gpr_slice_unref(creds_data);
175 4 : if (json != NULL) grpc_json_destroy(json);
176 4 : return result;
177 : }
178 :
179 3 : grpc_channel_credentials *grpc_google_default_credentials_create(void) {
180 3 : grpc_channel_credentials *result = NULL;
181 3 : grpc_call_credentials *call_creds = NULL;
182 :
183 3 : GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
184 :
185 3 : gpr_once_init(&g_once, init_default_credentials);
186 :
187 3 : gpr_mu_lock(&g_mu);
188 :
189 3 : if (default_credentials != NULL) {
190 0 : result = grpc_channel_credentials_ref(default_credentials);
191 0 : goto end;
192 : }
193 :
194 : /* First, try the environment variable. */
195 3 : call_creds = create_default_creds_from_path(
196 : gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR));
197 3 : if (call_creds != NULL) goto end;
198 :
199 : /* Then the well-known file. */
200 1 : call_creds = create_default_creds_from_path(
201 : grpc_get_well_known_google_credentials_file_path());
202 1 : if (call_creds != NULL) goto end;
203 :
204 : /* At last try to see if we're on compute engine (do the detection only once
205 : since it requires a network test). */
206 1 : if (!compute_engine_detection_done) {
207 1 : int need_compute_engine_creds = is_stack_running_on_compute_engine();
208 1 : compute_engine_detection_done = 1;
209 1 : if (need_compute_engine_creds) {
210 1 : call_creds = grpc_google_compute_engine_credentials_create(NULL);
211 : }
212 : }
213 :
214 : end:
215 3 : if (result == NULL) {
216 3 : if (call_creds != NULL) {
217 : /* Blend with default ssl credentials and add a global reference so that
218 : it
219 : can be cached and re-served. */
220 3 : grpc_channel_credentials *ssl_creds =
221 : grpc_ssl_credentials_create(NULL, NULL, NULL);
222 3 : default_credentials = grpc_channel_credentials_ref(
223 : grpc_composite_channel_credentials_create(ssl_creds, call_creds,
224 : NULL));
225 3 : GPR_ASSERT(default_credentials != NULL);
226 3 : grpc_channel_credentials_unref(ssl_creds);
227 3 : grpc_call_credentials_unref(call_creds);
228 3 : result = default_credentials;
229 : } else {
230 0 : gpr_log(GPR_ERROR, "Could not create google default credentials.");
231 : }
232 : }
233 3 : gpr_mu_unlock(&g_mu);
234 3 : return result;
235 : }
236 :
237 3 : void grpc_flush_cached_google_default_credentials(void) {
238 3 : gpr_once_init(&g_once, init_default_credentials);
239 3 : gpr_mu_lock(&g_mu);
240 3 : if (default_credentials != NULL) {
241 2 : grpc_channel_credentials_unref(default_credentials);
242 2 : default_credentials = NULL;
243 : }
244 3 : compute_engine_detection_done = 0;
245 3 : gpr_mu_unlock(&g_mu);
246 3 : }
247 :
248 : /* -- Well known credentials path. -- */
249 :
250 : static grpc_well_known_credentials_path_getter creds_path_getter = NULL;
251 :
252 3 : char *grpc_get_well_known_google_credentials_file_path(void) {
253 3 : if (creds_path_getter != NULL) return creds_path_getter();
254 2 : return grpc_get_well_known_google_credentials_file_path_impl();
255 : }
256 :
257 2 : void grpc_override_well_known_credentials_path_getter(
258 : grpc_well_known_credentials_path_getter getter) {
259 2 : creds_path_getter = getter;
260 2 : }
|