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