1 //
2 //
3 // Copyright 2016 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
20
21 #include <grpc/impl/channel_arg_names.h>
22 #include <grpc/support/alloc.h>
23 #include <grpc/support/port_platform.h>
24 #include <grpc/support/string_util.h>
25 #include <string.h>
26
27 #include <string>
28 #include <utility>
29
30 #include "absl/log/check.h"
31 #include "absl/log/log.h"
32 #include "absl/types/optional.h"
33 #include "src/core/lib/channel/channel_args.h"
34 #include "src/core/lib/debug/trace.h"
35 #include "src/core/lib/security/security_connector/ssl_utils.h"
36 #include "src/core/tsi/ssl/session_cache/ssl_session_cache.h"
37 #include "src/core/tsi/ssl_transport_security.h"
38 #include "src/core/tsi/transport_security_interface.h"
39
40 //
41 // SSL Channel Credentials.
42 //
43
grpc_ssl_credentials(const char * pem_root_certs,grpc_ssl_pem_key_cert_pair * pem_key_cert_pair,const grpc_ssl_verify_peer_options * verify_options)44 grpc_ssl_credentials::grpc_ssl_credentials(
45 const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
46 const grpc_ssl_verify_peer_options* verify_options) {
47 build_config(pem_root_certs, pem_key_cert_pair, verify_options);
48 // Use default (e.g. OS) root certificates if the user did not pass any root
49 // certificates.
50 if (config_.pem_root_certs == nullptr) {
51 const char* pem_root_certs =
52 grpc_core::DefaultSslRootStore::GetPemRootCerts();
53 if (pem_root_certs == nullptr) {
54 LOG(ERROR) << "Could not get default pem root certs.";
55 } else {
56 char* default_roots = gpr_strdup(pem_root_certs);
57 config_.pem_root_certs = default_roots;
58 root_store_ = grpc_core::DefaultSslRootStore::GetRootStore();
59 }
60 } else {
61 config_.pem_root_certs = config_.pem_root_certs;
62 root_store_ = nullptr;
63 }
64
65 client_handshaker_initialization_status_ = InitializeClientHandshakerFactory(
66 &config_, config_.pem_root_certs, root_store_, nullptr,
67 &client_handshaker_factory_);
68 }
69
~grpc_ssl_credentials()70 grpc_ssl_credentials::~grpc_ssl_credentials() {
71 gpr_free(config_.pem_root_certs);
72 grpc_tsi_ssl_pem_key_cert_pairs_destroy(config_.pem_key_cert_pair, 1);
73 if (config_.verify_options.verify_peer_destruct != nullptr) {
74 config_.verify_options.verify_peer_destruct(
75 config_.verify_options.verify_peer_callback_userdata);
76 }
77 tsi_ssl_client_handshaker_factory_unref(client_handshaker_factory_);
78 }
79
80 grpc_core::RefCountedPtr<grpc_channel_security_connector>
create_security_connector(grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,const char * target,grpc_core::ChannelArgs * args)81 grpc_ssl_credentials::create_security_connector(
82 grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
83 const char* target, grpc_core::ChannelArgs* args) {
84 if (config_.pem_root_certs == nullptr) {
85 LOG(ERROR) << "No root certs in config. Client-side security connector "
86 "must have root certs.";
87 return nullptr;
88 }
89 absl::optional<std::string> overridden_target_name =
90 args->GetOwnedString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG);
91 auto* ssl_session_cache = args->GetObject<tsi::SslSessionLRUCache>();
92 tsi_ssl_session_cache* session_cache =
93 ssl_session_cache == nullptr ? nullptr : ssl_session_cache->c_ptr();
94
95 grpc_core::RefCountedPtr<grpc_channel_security_connector> security_connector =
96 nullptr;
97 if (session_cache != nullptr) {
98 // We need a separate factory and SSL_CTX if there's a cache in the channel
99 // args. SSL_CTX should live with the factory and that should live on the
100 // credentials. However, there is a way to configure a session cache in the
101 // channel args, so that prevents us from also keeping the session cache at
102 // the credentials level. In the case of a session cache, we still need to
103 // keep a separate factory and SSL_CTX at the subchannel/security_connector
104 // level.
105 tsi_ssl_client_handshaker_factory* factory_with_cache = nullptr;
106 grpc_security_status status = InitializeClientHandshakerFactory(
107 &config_, config_.pem_root_certs, root_store_, session_cache,
108 &factory_with_cache);
109 if (status != GRPC_SECURITY_OK) {
110 LOG(ERROR) << "InitializeClientHandshakerFactory returned bad status.";
111 return nullptr;
112 }
113 security_connector = grpc_ssl_channel_security_connector_create(
114 this->Ref(), std::move(call_creds), &config_, target,
115 overridden_target_name.has_value() ? overridden_target_name->c_str()
116 : nullptr,
117 factory_with_cache);
118 tsi_ssl_client_handshaker_factory_unref(factory_with_cache);
119 } else {
120 if (client_handshaker_initialization_status_ != GRPC_SECURITY_OK) {
121 return nullptr;
122 }
123 security_connector = grpc_ssl_channel_security_connector_create(
124 this->Ref(), std::move(call_creds), &config_, target,
125 overridden_target_name.has_value() ? overridden_target_name->c_str()
126 : nullptr,
127 client_handshaker_factory_);
128 }
129
130 if (security_connector == nullptr) {
131 return security_connector;
132 }
133 *args = args->Set(GRPC_ARG_HTTP2_SCHEME, "https");
134 return security_connector;
135 }
136
Type()137 grpc_core::UniqueTypeName grpc_ssl_credentials::Type() {
138 static grpc_core::UniqueTypeName::Factory kFactory("Ssl");
139 return kFactory.Create();
140 }
141
build_config(const char * pem_root_certs,grpc_ssl_pem_key_cert_pair * pem_key_cert_pair,const grpc_ssl_verify_peer_options * verify_options)142 void grpc_ssl_credentials::build_config(
143 const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
144 const grpc_ssl_verify_peer_options* verify_options) {
145 config_.pem_root_certs = gpr_strdup(pem_root_certs);
146 if (pem_key_cert_pair != nullptr) {
147 CHECK_NE(pem_key_cert_pair->private_key, nullptr);
148 CHECK_NE(pem_key_cert_pair->cert_chain, nullptr);
149 config_.pem_key_cert_pair = static_cast<tsi_ssl_pem_key_cert_pair*>(
150 gpr_zalloc(sizeof(tsi_ssl_pem_key_cert_pair)));
151 config_.pem_key_cert_pair->cert_chain =
152 gpr_strdup(pem_key_cert_pair->cert_chain);
153 config_.pem_key_cert_pair->private_key =
154 gpr_strdup(pem_key_cert_pair->private_key);
155 } else {
156 config_.pem_key_cert_pair = nullptr;
157 }
158 if (verify_options != nullptr) {
159 memcpy(&config_.verify_options, verify_options,
160 sizeof(verify_peer_options));
161 } else {
162 // Otherwise set all options to default values
163 memset(&config_.verify_options, 0, sizeof(verify_peer_options));
164 }
165 }
166
set_min_tls_version(grpc_tls_version min_tls_version)167 void grpc_ssl_credentials::set_min_tls_version(
168 grpc_tls_version min_tls_version) {
169 config_.min_tls_version = min_tls_version;
170 }
171
set_max_tls_version(grpc_tls_version max_tls_version)172 void grpc_ssl_credentials::set_max_tls_version(
173 grpc_tls_version max_tls_version) {
174 config_.max_tls_version = max_tls_version;
175 }
176
InitializeClientHandshakerFactory(const grpc_ssl_config * config,const char * pem_root_certs,const tsi_ssl_root_certs_store * root_store,tsi_ssl_session_cache * ssl_session_cache,tsi_ssl_client_handshaker_factory ** handshaker_factory)177 grpc_security_status grpc_ssl_credentials::InitializeClientHandshakerFactory(
178 const grpc_ssl_config* config, const char* pem_root_certs,
179 const tsi_ssl_root_certs_store* root_store,
180 tsi_ssl_session_cache* ssl_session_cache,
181 tsi_ssl_client_handshaker_factory** handshaker_factory) {
182 // This class level factory can't have a session cache by design. If we want
183 // to init one with a cache we need to make a new one
184 if (client_handshaker_factory_ != nullptr && ssl_session_cache == nullptr) {
185 return GRPC_SECURITY_OK;
186 }
187
188 bool has_key_cert_pair = config->pem_key_cert_pair != nullptr &&
189 config->pem_key_cert_pair->private_key != nullptr &&
190 config->pem_key_cert_pair->cert_chain != nullptr;
191 tsi_ssl_client_handshaker_options options;
192 if (pem_root_certs == nullptr) {
193 LOG(ERROR) << "Handshaker factory creation failed. pem_root_certs cannot "
194 "be nullptr";
195 return GRPC_SECURITY_ERROR;
196 }
197 options.pem_root_certs = pem_root_certs;
198 options.root_store = root_store;
199 options.alpn_protocols =
200 grpc_fill_alpn_protocol_strings(&options.num_alpn_protocols);
201 if (has_key_cert_pair) {
202 options.pem_key_cert_pair = config->pem_key_cert_pair;
203 }
204 options.cipher_suites = grpc_get_ssl_cipher_suites();
205 options.session_cache = ssl_session_cache;
206 options.min_tls_version = grpc_get_tsi_tls_version(config->min_tls_version);
207 options.max_tls_version = grpc_get_tsi_tls_version(config->max_tls_version);
208 const tsi_result result =
209 tsi_create_ssl_client_handshaker_factory_with_options(&options,
210 handshaker_factory);
211 gpr_free(options.alpn_protocols);
212 if (result != TSI_OK) {
213 LOG(ERROR) << "Handshaker factory creation failed with "
214 << tsi_result_to_string(result);
215 return GRPC_SECURITY_ERROR;
216 }
217 return GRPC_SECURITY_OK;
218 }
219
220 // Deprecated in favor of grpc_ssl_credentials_create_ex. Will be removed
221 // once all of its call sites are migrated to grpc_ssl_credentials_create_ex.
grpc_ssl_credentials_create(const char * pem_root_certs,grpc_ssl_pem_key_cert_pair * pem_key_cert_pair,const verify_peer_options * verify_options,void * reserved)222 grpc_channel_credentials* grpc_ssl_credentials_create(
223 const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
224 const verify_peer_options* verify_options, void* reserved) {
225 GRPC_TRACE_LOG(api, INFO)
226 << "grpc_ssl_credentials_create(pem_root_certs=" << pem_root_certs
227 << ", pem_key_cert_pair=" << pem_key_cert_pair
228 << ", verify_options=" << verify_options << ", reserved=" << reserved
229 << ")";
230 CHECK_EQ(reserved, nullptr);
231
232 return new grpc_ssl_credentials(
233 pem_root_certs, pem_key_cert_pair,
234 reinterpret_cast<const grpc_ssl_verify_peer_options*>(verify_options));
235 }
236
grpc_ssl_credentials_create_ex(const char * pem_root_certs,grpc_ssl_pem_key_cert_pair * pem_key_cert_pair,const grpc_ssl_verify_peer_options * verify_options,void * reserved)237 grpc_channel_credentials* grpc_ssl_credentials_create_ex(
238 const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
239 const grpc_ssl_verify_peer_options* verify_options, void* reserved) {
240 GRPC_TRACE_LOG(api, INFO)
241 << "grpc_ssl_credentials_create(pem_root_certs=" << pem_root_certs
242 << ", pem_key_cert_pair=" << pem_key_cert_pair
243 << ", verify_options=" << verify_options << ", reserved=" << reserved
244 << ")";
245 CHECK_EQ(reserved, nullptr);
246
247 return new grpc_ssl_credentials(pem_root_certs, pem_key_cert_pair,
248 verify_options);
249 }
250
251 //
252 // SSL Server Credentials.
253 //
254
255 struct grpc_ssl_server_credentials_options {
256 grpc_ssl_client_certificate_request_type client_certificate_request;
257 grpc_ssl_server_certificate_config* certificate_config;
258 grpc_ssl_server_certificate_config_fetcher* certificate_config_fetcher;
259 };
260
grpc_ssl_server_credentials(const grpc_ssl_server_credentials_options & options)261 grpc_ssl_server_credentials::grpc_ssl_server_credentials(
262 const grpc_ssl_server_credentials_options& options) {
263 if (options.certificate_config_fetcher != nullptr) {
264 config_.client_certificate_request = options.client_certificate_request;
265 certificate_config_fetcher_ = *options.certificate_config_fetcher;
266 } else {
267 build_config(options.certificate_config->pem_root_certs,
268 options.certificate_config->pem_key_cert_pairs,
269 options.certificate_config->num_key_cert_pairs,
270 options.client_certificate_request);
271 }
272 }
273
~grpc_ssl_server_credentials()274 grpc_ssl_server_credentials::~grpc_ssl_server_credentials() {
275 grpc_tsi_ssl_pem_key_cert_pairs_destroy(config_.pem_key_cert_pairs,
276 config_.num_key_cert_pairs);
277 gpr_free(config_.pem_root_certs);
278 }
279 grpc_core::RefCountedPtr<grpc_server_security_connector>
create_security_connector(const grpc_core::ChannelArgs &)280 grpc_ssl_server_credentials::create_security_connector(
281 const grpc_core::ChannelArgs& /* args */) {
282 return grpc_ssl_server_security_connector_create(this->Ref());
283 }
284
Type()285 grpc_core::UniqueTypeName grpc_ssl_server_credentials::Type() {
286 static grpc_core::UniqueTypeName::Factory kFactory("Ssl");
287 return kFactory.Create();
288 }
289
grpc_convert_grpc_to_tsi_cert_pairs(const grpc_ssl_pem_key_cert_pair * pem_key_cert_pairs,size_t num_key_cert_pairs)290 tsi_ssl_pem_key_cert_pair* grpc_convert_grpc_to_tsi_cert_pairs(
291 const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
292 size_t num_key_cert_pairs) {
293 tsi_ssl_pem_key_cert_pair* tsi_pairs = nullptr;
294 if (num_key_cert_pairs > 0) {
295 CHECK_NE(pem_key_cert_pairs, nullptr);
296 tsi_pairs = static_cast<tsi_ssl_pem_key_cert_pair*>(
297 gpr_zalloc(num_key_cert_pairs * sizeof(tsi_ssl_pem_key_cert_pair)));
298 }
299 for (size_t i = 0; i < num_key_cert_pairs; i++) {
300 CHECK_NE(pem_key_cert_pairs[i].private_key, nullptr);
301 CHECK_NE(pem_key_cert_pairs[i].cert_chain, nullptr);
302 tsi_pairs[i].cert_chain = gpr_strdup(pem_key_cert_pairs[i].cert_chain);
303 tsi_pairs[i].private_key = gpr_strdup(pem_key_cert_pairs[i].private_key);
304 }
305 return tsi_pairs;
306 }
307
build_config(const char * pem_root_certs,grpc_ssl_pem_key_cert_pair * pem_key_cert_pairs,size_t num_key_cert_pairs,grpc_ssl_client_certificate_request_type client_certificate_request)308 void grpc_ssl_server_credentials::build_config(
309 const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
310 size_t num_key_cert_pairs,
311 grpc_ssl_client_certificate_request_type client_certificate_request) {
312 config_.client_certificate_request = client_certificate_request;
313 config_.pem_root_certs = gpr_strdup(pem_root_certs);
314 config_.pem_key_cert_pairs = grpc_convert_grpc_to_tsi_cert_pairs(
315 pem_key_cert_pairs, num_key_cert_pairs);
316 config_.num_key_cert_pairs = num_key_cert_pairs;
317 }
318
set_min_tls_version(grpc_tls_version min_tls_version)319 void grpc_ssl_server_credentials::set_min_tls_version(
320 grpc_tls_version min_tls_version) {
321 config_.min_tls_version = min_tls_version;
322 }
323
set_max_tls_version(grpc_tls_version max_tls_version)324 void grpc_ssl_server_credentials::set_max_tls_version(
325 grpc_tls_version max_tls_version) {
326 config_.max_tls_version = max_tls_version;
327 }
328
grpc_ssl_server_certificate_config_create(const char * pem_root_certs,const grpc_ssl_pem_key_cert_pair * pem_key_cert_pairs,size_t num_key_cert_pairs)329 grpc_ssl_server_certificate_config* grpc_ssl_server_certificate_config_create(
330 const char* pem_root_certs,
331 const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
332 size_t num_key_cert_pairs) {
333 grpc_ssl_server_certificate_config* config =
334 static_cast<grpc_ssl_server_certificate_config*>(
335 gpr_zalloc(sizeof(grpc_ssl_server_certificate_config)));
336 config->pem_root_certs = gpr_strdup(pem_root_certs);
337 if (num_key_cert_pairs > 0) {
338 CHECK_NE(pem_key_cert_pairs, nullptr);
339 config->pem_key_cert_pairs = static_cast<grpc_ssl_pem_key_cert_pair*>(
340 gpr_zalloc(num_key_cert_pairs * sizeof(grpc_ssl_pem_key_cert_pair)));
341 }
342 config->num_key_cert_pairs = num_key_cert_pairs;
343 for (size_t i = 0; i < num_key_cert_pairs; i++) {
344 CHECK_NE(pem_key_cert_pairs[i].private_key, nullptr);
345 CHECK_NE(pem_key_cert_pairs[i].cert_chain, nullptr);
346 config->pem_key_cert_pairs[i].cert_chain =
347 gpr_strdup(pem_key_cert_pairs[i].cert_chain);
348 config->pem_key_cert_pairs[i].private_key =
349 gpr_strdup(pem_key_cert_pairs[i].private_key);
350 }
351 return config;
352 }
353
grpc_ssl_server_certificate_config_destroy(grpc_ssl_server_certificate_config * config)354 void grpc_ssl_server_certificate_config_destroy(
355 grpc_ssl_server_certificate_config* config) {
356 if (config == nullptr) return;
357 for (size_t i = 0; i < config->num_key_cert_pairs; i++) {
358 gpr_free(const_cast<char*>(config->pem_key_cert_pairs[i].private_key));
359 gpr_free(const_cast<char*>(config->pem_key_cert_pairs[i].cert_chain));
360 }
361 gpr_free(config->pem_key_cert_pairs);
362 gpr_free(config->pem_root_certs);
363 gpr_free(config);
364 }
365
366 grpc_ssl_server_credentials_options*
grpc_ssl_server_credentials_create_options_using_config(grpc_ssl_client_certificate_request_type client_certificate_request,grpc_ssl_server_certificate_config * config)367 grpc_ssl_server_credentials_create_options_using_config(
368 grpc_ssl_client_certificate_request_type client_certificate_request,
369 grpc_ssl_server_certificate_config* config) {
370 grpc_ssl_server_credentials_options* options = nullptr;
371 if (config == nullptr) {
372 LOG(ERROR) << "Certificate config must not be NULL.";
373 goto done;
374 }
375 options = static_cast<grpc_ssl_server_credentials_options*>(
376 gpr_zalloc(sizeof(grpc_ssl_server_credentials_options)));
377 options->client_certificate_request = client_certificate_request;
378 options->certificate_config = config;
379 done:
380 return options;
381 }
382
383 grpc_ssl_server_credentials_options*
grpc_ssl_server_credentials_create_options_using_config_fetcher(grpc_ssl_client_certificate_request_type client_certificate_request,grpc_ssl_server_certificate_config_callback cb,void * user_data)384 grpc_ssl_server_credentials_create_options_using_config_fetcher(
385 grpc_ssl_client_certificate_request_type client_certificate_request,
386 grpc_ssl_server_certificate_config_callback cb, void* user_data) {
387 if (cb == nullptr) {
388 LOG(ERROR) << "Invalid certificate config callback parameter.";
389 return nullptr;
390 }
391
392 grpc_ssl_server_certificate_config_fetcher* fetcher =
393 static_cast<grpc_ssl_server_certificate_config_fetcher*>(
394 gpr_zalloc(sizeof(grpc_ssl_server_certificate_config_fetcher)));
395 fetcher->cb = cb;
396 fetcher->user_data = user_data;
397
398 grpc_ssl_server_credentials_options* options =
399 static_cast<grpc_ssl_server_credentials_options*>(
400 gpr_zalloc(sizeof(grpc_ssl_server_credentials_options)));
401 options->client_certificate_request = client_certificate_request;
402 options->certificate_config_fetcher = fetcher;
403
404 return options;
405 }
406
grpc_ssl_server_credentials_create(const char * pem_root_certs,grpc_ssl_pem_key_cert_pair * pem_key_cert_pairs,size_t num_key_cert_pairs,int force_client_auth,void * reserved)407 grpc_server_credentials* grpc_ssl_server_credentials_create(
408 const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
409 size_t num_key_cert_pairs, int force_client_auth, void* reserved) {
410 return grpc_ssl_server_credentials_create_ex(
411 pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs,
412 force_client_auth
413 ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
414 : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
415 reserved);
416 }
417
grpc_ssl_server_credentials_create_ex(const char * pem_root_certs,grpc_ssl_pem_key_cert_pair * pem_key_cert_pairs,size_t num_key_cert_pairs,grpc_ssl_client_certificate_request_type client_certificate_request,void * reserved)418 grpc_server_credentials* grpc_ssl_server_credentials_create_ex(
419 const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
420 size_t num_key_cert_pairs,
421 grpc_ssl_client_certificate_request_type client_certificate_request,
422 void* reserved) {
423 GRPC_TRACE_LOG(api, INFO)
424 << "grpc_ssl_server_credentials_create_ex(pem_root_certs="
425 << pem_root_certs << ", pem_key_cert_pairs=" << pem_key_cert_pairs
426 << ", num_key_cert_pairs=" << (unsigned long)num_key_cert_pairs
427 << ", client_certificate_request=" << client_certificate_request
428 << ", reserved=" << reserved << ")";
429 CHECK_EQ(reserved, nullptr);
430
431 grpc_ssl_server_certificate_config* cert_config =
432 grpc_ssl_server_certificate_config_create(
433 pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs);
434 grpc_ssl_server_credentials_options* options =
435 grpc_ssl_server_credentials_create_options_using_config(
436 client_certificate_request, cert_config);
437
438 return grpc_ssl_server_credentials_create_with_options(options);
439 }
440
grpc_ssl_server_credentials_create_with_options(grpc_ssl_server_credentials_options * options)441 grpc_server_credentials* grpc_ssl_server_credentials_create_with_options(
442 grpc_ssl_server_credentials_options* options) {
443 grpc_server_credentials* retval = nullptr;
444
445 if (options == nullptr) {
446 LOG(ERROR) << "Invalid options trying to create SSL server credentials.";
447 goto done;
448 }
449
450 if (options->certificate_config == nullptr &&
451 options->certificate_config_fetcher == nullptr) {
452 LOG(ERROR) << "SSL server credentials options must specify either "
453 "certificate config or fetcher.";
454 goto done;
455 } else if (options->certificate_config_fetcher != nullptr &&
456 options->certificate_config_fetcher->cb == nullptr) {
457 LOG(ERROR) << "Certificate config fetcher callback must not be NULL.";
458 goto done;
459 }
460
461 retval = new grpc_ssl_server_credentials(*options);
462
463 done:
464 grpc_ssl_server_credentials_options_destroy(options);
465 return retval;
466 }
467
grpc_ssl_server_credentials_options_destroy(grpc_ssl_server_credentials_options * o)468 void grpc_ssl_server_credentials_options_destroy(
469 grpc_ssl_server_credentials_options* o) {
470 if (o == nullptr) return;
471 gpr_free(o->certificate_config_fetcher);
472 grpc_ssl_server_certificate_config_destroy(o->certificate_config);
473 gpr_free(o);
474 }
475