xref: /aosp_15_r20/external/grpc-grpc/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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
15
16def _spawn_callback_in_thread(cb_func, args):
17  t = ForkManagedThread(target=cb_func, args=args)
18  t.setDaemon(True)
19  t.start()
20
21async_callback_func = _spawn_callback_in_thread
22
23def set_async_callback_func(callback_func):
24  global async_callback_func
25  async_callback_func = callback_func
26
27def _spawn_callback_async(callback, args):
28  async_callback_func(callback, args)
29
30
31cdef class CallCredentials:
32
33  cdef grpc_call_credentials *c(self) except *:
34    raise NotImplementedError()
35
36
37cdef int _get_metadata(void *state,
38                       grpc_auth_metadata_context context,
39                       grpc_credentials_plugin_metadata_cb cb,
40                       void *user_data,
41                       grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
42                       size_t *num_creds_md,
43                       grpc_status_code *status,
44                       const char **error_details) except * with gil:
45  cdef size_t metadata_count
46  cdef grpc_metadata *c_metadata
47  def callback(metadata, grpc_status_code status, bytes error_details):
48    cdef char* c_error_details = NULL
49    if error_details is not None:
50      c_error_details = <char*> error_details
51    if status == StatusCode.ok:
52      _store_c_metadata(metadata, &c_metadata, &metadata_count)
53      with nogil:
54        cb(user_data, c_metadata, metadata_count, status, NULL)
55      _release_c_metadata(c_metadata, metadata_count)
56    else:
57      with nogil:
58        cb(user_data, NULL, 0, status, c_error_details)
59  args = context.service_url, context.method_name, callback,
60  plugin = <object>state
61  if plugin._stored_ctx is not None:
62    plugin._stored_ctx.copy().run(_spawn_callback_async, plugin, args)
63  else:
64    _spawn_callback_async(<object>state, args)
65  return 0  # Asynchronous return
66
67
68cdef void _destroy(void *state) except * with gil:
69  cpython.Py_DECREF(<object>state)
70  grpc_shutdown()
71
72
73cdef class MetadataPluginCallCredentials(CallCredentials):
74
75  def __cinit__(self, metadata_plugin, name):
76    self._metadata_plugin = metadata_plugin
77    self._name = name
78
79  cdef grpc_call_credentials *c(self) except *:
80    cdef grpc_metadata_credentials_plugin c_metadata_plugin
81    c_metadata_plugin.get_metadata = _get_metadata
82    c_metadata_plugin.destroy = _destroy
83    c_metadata_plugin.state = <void *>self._metadata_plugin
84    c_metadata_plugin.type = self._name
85    cpython.Py_INCREF(self._metadata_plugin)
86    fork_handlers_and_grpc_init()
87    # TODO(yihuazhang): Expose min_security_level via the Python API so that
88    # applications can decide what minimum security level their plugins require.
89    return grpc_metadata_credentials_create_from_plugin(c_metadata_plugin, GRPC_PRIVACY_AND_INTEGRITY, NULL)
90
91
92cdef grpc_call_credentials *_composition(call_credentialses):
93  call_credentials_iterator = iter(call_credentialses)
94  cdef CallCredentials composition = next(call_credentials_iterator)
95  cdef grpc_call_credentials *c_composition = composition.c()
96  cdef CallCredentials additional_call_credentials
97  cdef grpc_call_credentials *c_additional_call_credentials
98  cdef grpc_call_credentials *c_next_composition
99  for additional_call_credentials in call_credentials_iterator:
100    c_additional_call_credentials = additional_call_credentials.c()
101    c_next_composition = grpc_composite_call_credentials_create(
102        c_composition, c_additional_call_credentials, NULL)
103    grpc_call_credentials_release(c_composition)
104    grpc_call_credentials_release(c_additional_call_credentials)
105    c_composition = c_next_composition
106  return c_composition
107
108
109cdef class CompositeCallCredentials(CallCredentials):
110
111  def __cinit__(self, call_credentialses):
112    self._call_credentialses = call_credentialses
113
114  cdef grpc_call_credentials *c(self) except *:
115    return _composition(self._call_credentialses)
116
117
118cdef class ChannelCredentials:
119
120  cdef grpc_channel_credentials *c(self) except *:
121    raise NotImplementedError()
122
123
124cdef class SSLSessionCacheLRU:
125
126  def __cinit__(self, capacity):
127    fork_handlers_and_grpc_init()
128    self._cache = grpc_ssl_session_cache_create_lru(capacity)
129
130  def __int__(self):
131    return <uintptr_t>self._cache
132
133  def __dealloc__(self):
134    if self._cache != NULL:
135        grpc_ssl_session_cache_destroy(self._cache)
136    grpc_shutdown()
137
138
139cdef class SSLChannelCredentials(ChannelCredentials):
140
141  def __cinit__(self, pem_root_certificates, private_key, certificate_chain):
142    if pem_root_certificates is not None and not isinstance(pem_root_certificates, bytes):
143      raise TypeError('expected certificate to be bytes, got %s' % (type(pem_root_certificates)))
144    self._pem_root_certificates = pem_root_certificates
145    self._private_key = private_key
146    self._certificate_chain = certificate_chain
147
148  cdef grpc_channel_credentials *c(self) except *:
149    cdef const char *c_pem_root_certificates
150    cdef grpc_ssl_pem_key_cert_pair c_pem_key_certificate_pair
151    if self._pem_root_certificates is None:
152      c_pem_root_certificates = NULL
153    else:
154      c_pem_root_certificates = self._pem_root_certificates
155    if self._private_key is None and self._certificate_chain is None:
156      with nogil:
157        return grpc_ssl_credentials_create(
158            c_pem_root_certificates, NULL, NULL, NULL)
159    else:
160      if self._private_key:
161        c_pem_key_certificate_pair.private_key = self._private_key
162      else:
163        c_pem_key_certificate_pair.private_key = NULL
164      if self._certificate_chain:
165        c_pem_key_certificate_pair.certificate_chain = self._certificate_chain
166      else:
167        c_pem_key_certificate_pair.certificate_chain = NULL
168      with nogil:
169        return grpc_ssl_credentials_create(
170            c_pem_root_certificates, &c_pem_key_certificate_pair, NULL, NULL)
171
172
173cdef class CompositeChannelCredentials(ChannelCredentials):
174
175  def __cinit__(self, call_credentialses, channel_credentials):
176    self._call_credentialses = call_credentialses
177    self._channel_credentials = channel_credentials
178
179  cdef grpc_channel_credentials *c(self) except *:
180    cdef grpc_channel_credentials *c_channel_credentials
181    c_channel_credentials = self._channel_credentials.c()
182    cdef grpc_call_credentials *c_call_credentials_composition = _composition(
183        self._call_credentialses)
184    cdef grpc_channel_credentials *composition
185    c_composition = grpc_composite_channel_credentials_create(
186        c_channel_credentials, c_call_credentials_composition, NULL)
187    grpc_channel_credentials_release(c_channel_credentials)
188    grpc_call_credentials_release(c_call_credentials_composition)
189    return c_composition
190
191
192cdef class XDSChannelCredentials(ChannelCredentials):
193
194    def __cinit__(self, fallback_credentials):
195        self._fallback_credentials = fallback_credentials
196
197    cdef grpc_channel_credentials *c(self) except *:
198      cdef grpc_channel_credentials *c_fallback_creds = self._fallback_credentials.c()
199      cdef grpc_channel_credentials *xds_creds = grpc_xds_credentials_create(c_fallback_creds)
200      grpc_channel_credentials_release(c_fallback_creds)
201      return xds_creds
202
203
204cdef class ServerCertificateConfig:
205
206  def __cinit__(self):
207    fork_handlers_and_grpc_init()
208    self.c_cert_config = NULL
209    self.c_pem_root_certs = NULL
210    self.c_ssl_pem_key_cert_pairs = NULL
211    self.references = []
212
213  def __dealloc__(self):
214    grpc_ssl_server_certificate_config_destroy(self.c_cert_config)
215    gpr_free(self.c_ssl_pem_key_cert_pairs)
216    grpc_shutdown()
217
218
219cdef class ServerCredentials:
220
221  def __cinit__(self):
222    fork_handlers_and_grpc_init()
223    self.c_credentials = NULL
224    self.references = []
225    self.initial_cert_config = None
226    self.cert_config_fetcher = None
227    self.initial_cert_config_fetched = False
228
229  def __dealloc__(self):
230    if self.c_credentials != NULL:
231      grpc_server_credentials_release(self.c_credentials)
232    grpc_shutdown()
233
234cdef const char* _get_c_pem_root_certs(pem_root_certs):
235  if pem_root_certs is None:
236    return NULL
237  else:
238    return pem_root_certs
239
240cdef grpc_ssl_pem_key_cert_pair* _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs):
241  # return a malloc'ed grpc_ssl_pem_key_cert_pair from a _list_ of SslPemKeyCertPair
242  for pair in pem_key_cert_pairs:
243    if not isinstance(pair, SslPemKeyCertPair):
244      raise TypeError("expected pem_key_cert_pairs to be sequence of "
245                      "SslPemKeyCertPair")
246  cdef size_t c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
247  cdef grpc_ssl_pem_key_cert_pair* c_ssl_pem_key_cert_pairs = NULL
248  with nogil:
249    c_ssl_pem_key_cert_pairs = (
250      <grpc_ssl_pem_key_cert_pair *>gpr_malloc(
251        sizeof(grpc_ssl_pem_key_cert_pair) * c_ssl_pem_key_cert_pairs_count))
252  for i in range(c_ssl_pem_key_cert_pairs_count):
253    c_ssl_pem_key_cert_pairs[i] = (
254      (<SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
255  return c_ssl_pem_key_cert_pairs
256
257def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
258                           bint force_client_auth):
259  pem_root_certs = str_to_bytes(pem_root_certs)
260  pem_key_cert_pairs = list(pem_key_cert_pairs)
261  cdef ServerCredentials credentials = ServerCredentials()
262  credentials.references.append(pem_root_certs)
263  credentials.references.append(pem_key_cert_pairs)
264  cdef const char * c_pem_root_certs = _get_c_pem_root_certs(pem_root_certs)
265  credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
266  credentials.c_ssl_pem_key_cert_pairs = _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs)
267  cdef grpc_ssl_server_certificate_config *c_cert_config = NULL
268  c_cert_config = grpc_ssl_server_certificate_config_create(
269    c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
270    credentials.c_ssl_pem_key_cert_pairs_count)
271  cdef grpc_ssl_server_credentials_options* c_options = NULL
272  # C-core assumes ownership of c_cert_config
273  c_options = grpc_ssl_server_credentials_create_options_using_config(
274    GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
275    if force_client_auth else
276    GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
277    c_cert_config)
278  # C-core assumes ownership of c_options
279  credentials.c_credentials = grpc_ssl_server_credentials_create_with_options(c_options)
280  return credentials
281
282def server_certificate_config_ssl(pem_root_certs, pem_key_cert_pairs):
283  pem_root_certs = str_to_bytes(pem_root_certs)
284  pem_key_cert_pairs = list(pem_key_cert_pairs)
285  cdef ServerCertificateConfig cert_config = ServerCertificateConfig()
286  cert_config.references.append(pem_root_certs)
287  cert_config.references.append(pem_key_cert_pairs)
288  cert_config.c_pem_root_certs = _get_c_pem_root_certs(pem_root_certs)
289  cert_config.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
290  cert_config.c_ssl_pem_key_cert_pairs = _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs)
291  cert_config.c_cert_config = grpc_ssl_server_certificate_config_create(
292    cert_config.c_pem_root_certs, cert_config.c_ssl_pem_key_cert_pairs,
293    cert_config.c_ssl_pem_key_cert_pairs_count)
294  return cert_config
295
296def server_credentials_ssl_dynamic_cert_config(initial_cert_config,
297                                               cert_config_fetcher,
298                                               bint force_client_auth):
299  if not isinstance(initial_cert_config, grpc.ServerCertificateConfiguration):
300    raise TypeError(
301        'initial_cert_config must be a grpc.ServerCertificateConfiguration')
302  if not callable(cert_config_fetcher):
303    raise TypeError('cert_config_fetcher must be callable')
304  cdef ServerCredentials credentials = ServerCredentials()
305  credentials.initial_cert_config = initial_cert_config
306  credentials.cert_config_fetcher = cert_config_fetcher
307  cdef grpc_ssl_server_credentials_options* c_options = NULL
308  c_options = grpc_ssl_server_credentials_create_options_using_config_fetcher(
309    GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
310    if force_client_auth else
311    GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
312    _server_cert_config_fetcher_wrapper,
313    <void*>credentials)
314  # C-core assumes ownership of c_options
315  credentials.c_credentials = grpc_ssl_server_credentials_create_with_options(c_options)
316  return credentials
317
318cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapper(
319        void* user_data, grpc_ssl_server_certificate_config **config) noexcept with gil:
320  # This is a credentials.ServerCertificateConfig
321  cdef ServerCertificateConfig cert_config = None
322  if not user_data:
323    raise ValueError('internal error: user_data must be specified')
324  credentials = <ServerCredentials>user_data
325  if not credentials.initial_cert_config_fetched:
326    # C-core is asking for the initial cert config
327    credentials.initial_cert_config_fetched = True
328    cert_config = credentials.initial_cert_config._certificate_configuration
329  else:
330    user_cb = credentials.cert_config_fetcher
331    try:
332      cert_config_wrapper = user_cb()
333    except Exception:
334      _LOGGER.exception('Error fetching certificate config')
335      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
336    if cert_config_wrapper is None:
337      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED
338    elif not isinstance(
339        cert_config_wrapper, grpc.ServerCertificateConfiguration):
340      _LOGGER.error(
341          'Error fetching certificate configuration: certificate '
342          'configuration must be of type grpc.ServerCertificateConfiguration, '
343          'not %s' % type(cert_config_wrapper).__name__)
344      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
345    else:
346      cert_config = cert_config_wrapper._certificate_configuration
347  config[0] = <grpc_ssl_server_certificate_config*>cert_config.c_cert_config
348  # our caller will assume ownership of memory, so we have to recreate
349  # a copy of c_cert_config here
350  cert_config.c_cert_config = grpc_ssl_server_certificate_config_create(
351      cert_config.c_pem_root_certs, cert_config.c_ssl_pem_key_cert_pairs,
352      cert_config.c_ssl_pem_key_cert_pairs_count)
353  return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW
354
355
356class LocalConnectionType:
357  uds = UDS
358  local_tcp = LOCAL_TCP
359
360cdef class LocalChannelCredentials(ChannelCredentials):
361
362  def __cinit__(self, grpc_local_connect_type local_connect_type):
363    self._local_connect_type = local_connect_type
364
365  cdef grpc_channel_credentials *c(self) except *:
366    cdef grpc_local_connect_type local_connect_type
367    local_connect_type = self._local_connect_type
368    return grpc_local_credentials_create(local_connect_type)
369
370def channel_credentials_local(grpc_local_connect_type local_connect_type):
371  return LocalChannelCredentials(local_connect_type)
372
373cdef class InsecureChannelCredentials(ChannelCredentials):
374
375  cdef grpc_channel_credentials *c(self) except *:
376    return grpc_insecure_credentials_create()
377
378def channel_credentials_insecure():
379  return InsecureChannelCredentials()
380
381def server_credentials_local(grpc_local_connect_type local_connect_type):
382  cdef ServerCredentials credentials = ServerCredentials()
383  credentials.c_credentials = grpc_local_server_credentials_create(local_connect_type)
384  return credentials
385
386def xds_server_credentials(ServerCredentials fallback_credentials):
387  cdef ServerCredentials credentials = ServerCredentials()
388  credentials.c_credentials = grpc_xds_server_credentials_create(fallback_credentials.c_credentials)
389  # NOTE: We do not need to call grpc_server_credentials_release on the
390  # fallback credentials here because this will be done by the __dealloc__
391  # method of its Cython wrapper.
392  return credentials
393
394def insecure_server_credentials():
395  cdef ServerCredentials credentials = ServerCredentials()
396  credentials.c_credentials = grpc_insecure_server_credentials_create()
397  return credentials
398
399cdef class ALTSChannelCredentials(ChannelCredentials):
400
401  def __cinit__(self, list service_accounts):
402    self.c_options = grpc_alts_credentials_client_options_create()
403    cdef str account
404    for account in service_accounts:
405      grpc_alts_credentials_client_options_add_target_service_account(
406          self.c_options, str_to_bytes(account))
407
408  def __dealloc__(self):
409    if self.c_options != NULL:
410      grpc_alts_credentials_options_destroy(self.c_options)
411
412  cdef grpc_channel_credentials *c(self) except *:
413    return grpc_alts_credentials_create(self.c_options)
414
415
416def channel_credentials_alts(list service_accounts):
417  return ALTSChannelCredentials(service_accounts)
418
419
420def server_credentials_alts():
421  cdef ServerCredentials credentials = ServerCredentials()
422  cdef grpc_alts_credentials_options* c_options = grpc_alts_credentials_server_options_create()
423  credentials.c_credentials = grpc_alts_server_credentials_create(c_options)
424  # Options can be destroyed as deep copy was performed.
425  grpc_alts_credentials_options_destroy(c_options)
426  return credentials
427
428
429cdef class ComputeEngineChannelCredentials(ChannelCredentials):
430  cdef grpc_channel_credentials* _c_creds
431  cdef grpc_call_credentials* _call_creds
432
433  def __cinit__(self, CallCredentials call_creds):
434    self._c_creds = NULL
435    self._call_creds = call_creds.c()
436    if self._call_creds == NULL:
437      raise ValueError("Call credentials may not be NULL.")
438
439  cdef grpc_channel_credentials *c(self) except *:
440    with nogil:
441      self._c_creds = grpc_google_default_credentials_create(self._call_creds)
442      return self._c_creds
443
444
445def channel_credentials_compute_engine(call_creds):
446  return ComputeEngineChannelCredentials(call_creds)
447