• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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