• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import atexit
16
17def _spawn_callback_in_thread(cb_func, args):
18  t = ForkManagedThread(target=cb_func, args=args)
19  t.setDaemon(True)
20  t.start()
21
22async_callback_func = _spawn_callback_in_thread
23
24def set_async_callback_func(callback_func):
25  global async_callback_func
26  async_callback_func = callback_func
27
28def _spawn_callback_async(callback, args):
29  async_callback_func(callback, args)
30
31
32cdef class CallCredentials:
33
34  cdef grpc_call_credentials *c(self) except *:
35    raise NotImplementedError()
36
37
38cdef int _get_metadata(void *state,
39                       grpc_auth_metadata_context context,
40                       grpc_credentials_plugin_metadata_cb cb,
41                       void *user_data,
42                       grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
43                       size_t *num_creds_md,
44                       grpc_status_code *status,
45                       const char **error_details) except -1 with gil:
46  cdef size_t metadata_count
47  cdef grpc_metadata *c_metadata
48  def callback(metadata, grpc_status_code status, bytes error_details):
49    cdef char* c_error_details = NULL
50    if error_details is not None:
51      c_error_details = <char*> error_details
52    if status == StatusCode.ok:
53      _store_c_metadata(metadata, &c_metadata, &metadata_count)
54      with nogil:
55        cb(user_data, c_metadata, metadata_count, status, NULL)
56      _release_c_metadata(c_metadata, metadata_count)
57    else:
58      with nogil:
59        cb(user_data, NULL, 0, status, c_error_details)
60  args = context.service_url, context.method_name, callback,
61  plugin = <object>state
62  if plugin._stored_ctx is not None:
63    plugin._stored_ctx.copy().run(_spawn_callback_async, plugin, args)
64  else:
65    _spawn_callback_async(<object>state, args)
66  return 0  # Asynchronous return
67
68
69# Protects access to GIL from _destroy() and to g_shutting_down.
70# Do NOT hold this while holding GIL to prevent a deadlock.
71cdef mutex g_shutdown_mu
72# Number of C-core clean up calls in progress. Set to -1 when Python is shutting
73# down.
74cdef int g_shutting_down = 0
75
76# This is called by C-core when the plugin is destroyed, which may race between
77# GIL destruction during process shutdown. Since GIL destruction happens after
78# Python's exit handlers, we mark that Python is shutting down from an exit
79# handler and don't grab GIL in this function afterwards using a C mutex.
80cdef void _destroy(void *state) noexcept nogil:
81  global g_shutdown_mu
82  global g_shutting_down
83  g_shutdown_mu.lock()
84  if g_shutting_down > -1:
85    g_shutting_down += 1
86    g_shutdown_mu.unlock()
87    with gil:
88      cpython.Py_DECREF(<object>state)
89    g_shutdown_mu.lock()
90    g_shutting_down -= 1
91  g_shutdown_mu.unlock()
92  grpc_shutdown()
93
94
95g_shutdown_handler_registered = False
96
97def _maybe_register_shutdown_handler():
98  global g_shutdown_handler_registered
99  if g_shutdown_handler_registered:
100    return
101  g_shutdown_handler_registered = True
102  atexit.register(_on_shutdown)
103
104cdef void _on_shutdown() noexcept nogil:
105  global g_shutdown_mu
106  global g_shutting_down
107  # Wait for up to ~2s if C-core is still cleaning up.
108  cdef int wait_ms = 10
109  while wait_ms < 1500:
110    g_shutdown_mu.lock()
111    if g_shutting_down == 0:
112      g_shutting_down = -1
113      g_shutdown_mu.unlock()
114      return
115    g_shutdown_mu.unlock()
116    with gil:
117      time.sleep(wait_ms / 1000)
118    wait_ms = wait_ms * 2
119  with gil:
120    _LOGGER.error('Timed out waiting for C-core clean-up')
121
122cdef class MetadataPluginCallCredentials(CallCredentials):
123
124  def __cinit__(self, metadata_plugin, name):
125    self._metadata_plugin = metadata_plugin
126    self._name = name
127
128  cdef grpc_call_credentials *c(self) except *:
129    cdef grpc_metadata_credentials_plugin c_metadata_plugin
130    c_metadata_plugin.get_metadata = _get_metadata
131    c_metadata_plugin.destroy = _destroy
132    c_metadata_plugin.state = <void *>self._metadata_plugin
133    c_metadata_plugin.type = self._name
134    cpython.Py_INCREF(self._metadata_plugin)
135    _maybe_register_shutdown_handler()
136    fork_handlers_and_grpc_init()
137    # TODO(yihuazhang): Expose min_security_level via the Python API so that
138    # applications can decide what minimum security level their plugins require.
139    return grpc_metadata_credentials_create_from_plugin(c_metadata_plugin, GRPC_PRIVACY_AND_INTEGRITY, NULL)
140
141
142cdef grpc_call_credentials *_composition(call_credentialses):
143  call_credentials_iterator = iter(call_credentialses)
144  cdef CallCredentials composition = next(call_credentials_iterator)
145  cdef grpc_call_credentials *c_composition = composition.c()
146  cdef CallCredentials additional_call_credentials
147  cdef grpc_call_credentials *c_additional_call_credentials
148  cdef grpc_call_credentials *c_next_composition
149  for additional_call_credentials in call_credentials_iterator:
150    c_additional_call_credentials = additional_call_credentials.c()
151    c_next_composition = grpc_composite_call_credentials_create(
152        c_composition, c_additional_call_credentials, NULL)
153    grpc_call_credentials_release(c_composition)
154    grpc_call_credentials_release(c_additional_call_credentials)
155    c_composition = c_next_composition
156  return c_composition
157
158
159cdef class CompositeCallCredentials(CallCredentials):
160
161  def __cinit__(self, call_credentialses):
162    self._call_credentialses = call_credentialses
163
164  cdef grpc_call_credentials *c(self) except *:
165    return _composition(self._call_credentialses)
166
167
168cdef class ChannelCredentials:
169
170  cdef grpc_channel_credentials *c(self) except *:
171    raise NotImplementedError()
172
173
174cdef class SSLSessionCacheLRU:
175
176  def __cinit__(self, capacity):
177    fork_handlers_and_grpc_init()
178    self._cache = grpc_ssl_session_cache_create_lru(capacity)
179
180  def __int__(self):
181    return <uintptr_t>self._cache
182
183  def __dealloc__(self):
184    if self._cache != NULL:
185        grpc_ssl_session_cache_destroy(self._cache)
186    grpc_shutdown()
187
188
189cdef class SSLChannelCredentials(ChannelCredentials):
190
191  def __cinit__(self, pem_root_certificates, private_key, certificate_chain):
192    if pem_root_certificates is not None and not isinstance(pem_root_certificates, bytes):
193      raise TypeError('expected certificate to be bytes, got %s' % (type(pem_root_certificates)))
194    self._pem_root_certificates = pem_root_certificates
195    self._private_key = private_key
196    self._certificate_chain = certificate_chain
197
198  cdef grpc_channel_credentials *c(self) except *:
199    cdef const char *c_pem_root_certificates
200    cdef grpc_ssl_pem_key_cert_pair c_pem_key_certificate_pair
201    if self._pem_root_certificates is None:
202      c_pem_root_certificates = NULL
203    else:
204      c_pem_root_certificates = self._pem_root_certificates
205    if self._private_key is None and self._certificate_chain is None:
206      with nogil:
207        return grpc_ssl_credentials_create(
208            c_pem_root_certificates, NULL, NULL, NULL)
209    else:
210      if self._private_key:
211        c_pem_key_certificate_pair.private_key = self._private_key
212      else:
213        c_pem_key_certificate_pair.private_key = NULL
214      if self._certificate_chain:
215        c_pem_key_certificate_pair.certificate_chain = self._certificate_chain
216      else:
217        c_pem_key_certificate_pair.certificate_chain = NULL
218      with nogil:
219        return grpc_ssl_credentials_create(
220            c_pem_root_certificates, &c_pem_key_certificate_pair, NULL, NULL)
221
222
223cdef class CompositeChannelCredentials(ChannelCredentials):
224
225  def __cinit__(self, call_credentialses, channel_credentials):
226    self._call_credentialses = call_credentialses
227    self._channel_credentials = channel_credentials
228
229  cdef grpc_channel_credentials *c(self) except *:
230    cdef grpc_channel_credentials *c_channel_credentials
231    c_channel_credentials = self._channel_credentials.c()
232    cdef grpc_call_credentials *c_call_credentials_composition = _composition(
233        self._call_credentialses)
234    cdef grpc_channel_credentials *composition
235    c_composition = grpc_composite_channel_credentials_create(
236        c_channel_credentials, c_call_credentials_composition, NULL)
237    grpc_channel_credentials_release(c_channel_credentials)
238    grpc_call_credentials_release(c_call_credentials_composition)
239    return c_composition
240
241
242cdef class XDSChannelCredentials(ChannelCredentials):
243
244    def __cinit__(self, fallback_credentials):
245        self._fallback_credentials = fallback_credentials
246
247    cdef grpc_channel_credentials *c(self) except *:
248      cdef grpc_channel_credentials *c_fallback_creds = self._fallback_credentials.c()
249      cdef grpc_channel_credentials *xds_creds = grpc_xds_credentials_create(c_fallback_creds)
250      grpc_channel_credentials_release(c_fallback_creds)
251      return xds_creds
252
253
254cdef class ServerCertificateConfig:
255
256  def __cinit__(self):
257    fork_handlers_and_grpc_init()
258    self.c_cert_config = NULL
259    self.c_pem_root_certs = NULL
260    self.c_ssl_pem_key_cert_pairs = NULL
261    self.references = []
262
263  def __dealloc__(self):
264    grpc_ssl_server_certificate_config_destroy(self.c_cert_config)
265    gpr_free(self.c_ssl_pem_key_cert_pairs)
266    grpc_shutdown()
267
268
269cdef class ServerCredentials:
270
271  def __cinit__(self):
272    fork_handlers_and_grpc_init()
273    self.c_credentials = NULL
274    self.references = []
275    self.initial_cert_config = None
276    self.cert_config_fetcher = None
277    self.initial_cert_config_fetched = False
278
279  def __dealloc__(self):
280    if self.c_credentials != NULL:
281      grpc_server_credentials_release(self.c_credentials)
282    grpc_shutdown()
283
284cdef const char* _get_c_pem_root_certs(pem_root_certs):
285  if pem_root_certs is None:
286    return NULL
287  else:
288    return pem_root_certs
289
290cdef grpc_ssl_pem_key_cert_pair* _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs):
291  # return a malloc'ed grpc_ssl_pem_key_cert_pair from a _list_ of SslPemKeyCertPair
292  for pair in pem_key_cert_pairs:
293    if not isinstance(pair, SslPemKeyCertPair):
294      raise TypeError("expected pem_key_cert_pairs to be sequence of "
295                      "SslPemKeyCertPair")
296  cdef size_t c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
297  cdef grpc_ssl_pem_key_cert_pair* c_ssl_pem_key_cert_pairs = NULL
298  with nogil:
299    c_ssl_pem_key_cert_pairs = (
300      <grpc_ssl_pem_key_cert_pair *>gpr_malloc(
301        sizeof(grpc_ssl_pem_key_cert_pair) * c_ssl_pem_key_cert_pairs_count))
302  for i in range(c_ssl_pem_key_cert_pairs_count):
303    c_ssl_pem_key_cert_pairs[i] = (
304      (<SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
305  return c_ssl_pem_key_cert_pairs
306
307def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
308                           bint force_client_auth):
309  pem_root_certs = str_to_bytes(pem_root_certs)
310  pem_key_cert_pairs = list(pem_key_cert_pairs)
311  cdef ServerCredentials credentials = ServerCredentials()
312  credentials.references.append(pem_root_certs)
313  credentials.references.append(pem_key_cert_pairs)
314  cdef const char * c_pem_root_certs = _get_c_pem_root_certs(pem_root_certs)
315  credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
316  credentials.c_ssl_pem_key_cert_pairs = _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs)
317  cdef grpc_ssl_server_certificate_config *c_cert_config = NULL
318  c_cert_config = grpc_ssl_server_certificate_config_create(
319    c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
320    credentials.c_ssl_pem_key_cert_pairs_count)
321  cdef grpc_ssl_server_credentials_options* c_options = NULL
322  # C-core assumes ownership of c_cert_config
323  c_options = grpc_ssl_server_credentials_create_options_using_config(
324    GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
325    if force_client_auth else
326    GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
327    c_cert_config)
328  # C-core assumes ownership of c_options
329  credentials.c_credentials = grpc_ssl_server_credentials_create_with_options(c_options)
330  return credentials
331
332def server_certificate_config_ssl(pem_root_certs, pem_key_cert_pairs):
333  pem_root_certs = str_to_bytes(pem_root_certs)
334  pem_key_cert_pairs = list(pem_key_cert_pairs)
335  cdef ServerCertificateConfig cert_config = ServerCertificateConfig()
336  cert_config.references.append(pem_root_certs)
337  cert_config.references.append(pem_key_cert_pairs)
338  cert_config.c_pem_root_certs = _get_c_pem_root_certs(pem_root_certs)
339  cert_config.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
340  cert_config.c_ssl_pem_key_cert_pairs = _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs)
341  cert_config.c_cert_config = grpc_ssl_server_certificate_config_create(
342    cert_config.c_pem_root_certs, cert_config.c_ssl_pem_key_cert_pairs,
343    cert_config.c_ssl_pem_key_cert_pairs_count)
344  return cert_config
345
346def server_credentials_ssl_dynamic_cert_config(initial_cert_config,
347                                               cert_config_fetcher,
348                                               bint force_client_auth):
349  if not isinstance(initial_cert_config, grpc.ServerCertificateConfiguration):
350    raise TypeError(
351        'initial_cert_config must be a grpc.ServerCertificateConfiguration')
352  if not callable(cert_config_fetcher):
353    raise TypeError('cert_config_fetcher must be callable')
354  cdef ServerCredentials credentials = ServerCredentials()
355  credentials.initial_cert_config = initial_cert_config
356  credentials.cert_config_fetcher = cert_config_fetcher
357  cdef grpc_ssl_server_credentials_options* c_options = NULL
358  c_options = grpc_ssl_server_credentials_create_options_using_config_fetcher(
359    GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
360    if force_client_auth else
361    GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
362    _server_cert_config_fetcher_wrapper,
363    <void*>credentials)
364  # C-core assumes ownership of c_options
365  credentials.c_credentials = grpc_ssl_server_credentials_create_with_options(c_options)
366  return credentials
367
368cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapper(
369        void* user_data, grpc_ssl_server_certificate_config **config) noexcept with gil:
370  # This is a credentials.ServerCertificateConfig
371  cdef ServerCertificateConfig cert_config = None
372  if not user_data:
373    raise ValueError('internal error: user_data must be specified')
374  credentials = <ServerCredentials>user_data
375  if not credentials.initial_cert_config_fetched:
376    # C-core is asking for the initial cert config
377    credentials.initial_cert_config_fetched = True
378    cert_config = credentials.initial_cert_config._certificate_configuration
379  else:
380    user_cb = credentials.cert_config_fetcher
381    try:
382      cert_config_wrapper = user_cb()
383    except Exception:
384      _LOGGER.exception('Error fetching certificate config')
385      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
386    if cert_config_wrapper is None:
387      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED
388    elif not isinstance(
389        cert_config_wrapper, grpc.ServerCertificateConfiguration):
390      _LOGGER.error(
391          'Error fetching certificate configuration: certificate '
392          'configuration must be of type grpc.ServerCertificateConfiguration, '
393          'not %s' % type(cert_config_wrapper).__name__)
394      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
395    else:
396      cert_config = cert_config_wrapper._certificate_configuration
397  config[0] = <grpc_ssl_server_certificate_config*>cert_config.c_cert_config
398  # our caller will assume ownership of memory, so we have to recreate
399  # a copy of c_cert_config here
400  cert_config.c_cert_config = grpc_ssl_server_certificate_config_create(
401      cert_config.c_pem_root_certs, cert_config.c_ssl_pem_key_cert_pairs,
402      cert_config.c_ssl_pem_key_cert_pairs_count)
403  return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW
404
405
406class LocalConnectionType:
407  uds = UDS
408  local_tcp = LOCAL_TCP
409
410cdef class LocalChannelCredentials(ChannelCredentials):
411
412  def __cinit__(self, grpc_local_connect_type local_connect_type):
413    self._local_connect_type = local_connect_type
414
415  cdef grpc_channel_credentials *c(self) except *:
416    cdef grpc_local_connect_type local_connect_type
417    local_connect_type = self._local_connect_type
418    return grpc_local_credentials_create(local_connect_type)
419
420def channel_credentials_local(grpc_local_connect_type local_connect_type):
421  return LocalChannelCredentials(local_connect_type)
422
423cdef class InsecureChannelCredentials(ChannelCredentials):
424
425  cdef grpc_channel_credentials *c(self) except *:
426    return grpc_insecure_credentials_create()
427
428def channel_credentials_insecure():
429  return InsecureChannelCredentials()
430
431def server_credentials_local(grpc_local_connect_type local_connect_type):
432  cdef ServerCredentials credentials = ServerCredentials()
433  credentials.c_credentials = grpc_local_server_credentials_create(local_connect_type)
434  return credentials
435
436def xds_server_credentials(ServerCredentials fallback_credentials):
437  cdef ServerCredentials credentials = ServerCredentials()
438  credentials.c_credentials = grpc_xds_server_credentials_create(fallback_credentials.c_credentials)
439  # NOTE: We do not need to call grpc_server_credentials_release on the
440  # fallback credentials here because this will be done by the __dealloc__
441  # method of its Cython wrapper.
442  return credentials
443
444def insecure_server_credentials():
445  cdef ServerCredentials credentials = ServerCredentials()
446  credentials.c_credentials = grpc_insecure_server_credentials_create()
447  return credentials
448
449cdef class ALTSChannelCredentials(ChannelCredentials):
450
451  def __cinit__(self, list service_accounts):
452    self.c_options = grpc_alts_credentials_client_options_create()
453    cdef str account
454    for account in service_accounts:
455      grpc_alts_credentials_client_options_add_target_service_account(
456          self.c_options, str_to_bytes(account))
457
458  def __dealloc__(self):
459    if self.c_options != NULL:
460      grpc_alts_credentials_options_destroy(self.c_options)
461
462  cdef grpc_channel_credentials *c(self) except *:
463    return grpc_alts_credentials_create(self.c_options)
464
465
466def channel_credentials_alts(list service_accounts):
467  return ALTSChannelCredentials(service_accounts)
468
469
470def server_credentials_alts():
471  cdef ServerCredentials credentials = ServerCredentials()
472  cdef grpc_alts_credentials_options* c_options = grpc_alts_credentials_server_options_create()
473  credentials.c_credentials = grpc_alts_server_credentials_create(c_options)
474  # Options can be destroyed as deep copy was performed.
475  grpc_alts_credentials_options_destroy(c_options)
476  return credentials
477
478
479cdef class ComputeEngineChannelCredentials(ChannelCredentials):
480  cdef grpc_channel_credentials* _c_creds
481  cdef grpc_call_credentials* _call_creds
482
483  def __cinit__(self, CallCredentials call_creds):
484    self._c_creds = NULL
485    self._call_creds = call_creds.c()
486    if self._call_creds == NULL:
487      raise ValueError("Call credentials may not be NULL.")
488
489  cdef grpc_channel_credentials *c(self) except *:
490    with nogil:
491      self._c_creds = grpc_google_default_credentials_create(self._call_creds)
492      return self._c_creds
493
494
495def channel_credentials_compute_engine(call_creds):
496  return ComputeEngineChannelCredentials(call_creds)
497