| /* |
| * |
| * Copyright 2015 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H |
| #define GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include <stdint.h> |
| #include <stdlib.h> |
| |
| #include "src/core/lib/debug/trace.h" |
| |
| /* --- tsi result --- */ |
| |
| typedef enum { |
| TSI_OK = 0, |
| TSI_UNKNOWN_ERROR = 1, |
| TSI_INVALID_ARGUMENT = 2, |
| TSI_PERMISSION_DENIED = 3, |
| TSI_INCOMPLETE_DATA = 4, |
| TSI_FAILED_PRECONDITION = 5, |
| TSI_UNIMPLEMENTED = 6, |
| TSI_INTERNAL_ERROR = 7, |
| TSI_DATA_CORRUPTED = 8, |
| TSI_NOT_FOUND = 9, |
| TSI_PROTOCOL_FAILURE = 10, |
| TSI_HANDSHAKE_IN_PROGRESS = 11, |
| TSI_OUT_OF_RESOURCES = 12, |
| TSI_ASYNC = 13, |
| TSI_HANDSHAKE_SHUTDOWN = 14, |
| TSI_CLOSE_NOTIFY = 15, // Indicates that the connection should be closed. |
| TSI_DRAIN_BUFFER = 16, // Indicates that the buffer used to store handshake |
| // data should be drained. |
| } tsi_result; |
| |
| typedef enum { |
| TSI_SECURITY_MIN, |
| TSI_SECURITY_NONE = TSI_SECURITY_MIN, |
| TSI_INTEGRITY_ONLY, |
| TSI_PRIVACY_AND_INTEGRITY, |
| TSI_SECURITY_MAX = TSI_PRIVACY_AND_INTEGRITY, |
| } tsi_security_level; |
| |
| typedef enum { |
| // Default option |
| TSI_DONT_REQUEST_CLIENT_CERTIFICATE, |
| TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, |
| TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, |
| TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, |
| TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY, |
| } tsi_client_certificate_request_type; |
| |
| typedef enum { |
| // TSI implementation provides a normal frame protector. The caller |
| // should invoke tsi_handshaker_result_create_frame_protector() to |
| // generate the frame protector. |
| TSI_FRAME_PROTECTOR_NORMAL, |
| // TSI implementation provides a zero-copy frame protector. The caller |
| // should invoke tsi_handshaker_result_create_zero_copy_grpc_protector() |
| // to generate the frame protector. |
| TSI_FRAME_PROTECTOR_ZERO_COPY, |
| // TSI implementation provides both normal and zero-copy frame protectors. |
| // The caller should invoke either |
| // tsi_handshaker_result_create_frame_protector() or |
| // tsi_handshaker_result_create_zero_copy_grpc_protector() to generate |
| // the frame protector. |
| TSI_FRAME_PROTECTOR_NORMAL_OR_ZERO_COPY, |
| // TSI implementation does not provide any frame protector. This means |
| // that it is safe for the caller to send bytes unprotected on the wire. |
| TSI_FRAME_PROTECTOR_NONE, |
| } tsi_frame_protector_type; |
| |
| typedef enum { |
| TSI_TLS1_2, |
| TSI_TLS1_3, |
| } tsi_tls_version; |
| |
| const char* tsi_result_to_string(tsi_result result); |
| const char* tsi_security_level_to_string(tsi_security_level security_level); |
| |
| /* --- tsi tracing --- */ |
| |
| extern grpc_core::TraceFlag tsi_tracing_enabled; |
| |
| /* -- tsi_zero_copy_grpc_protector object -- |
| |
| This object protects and unprotects grpc slice buffers with zero or minimized |
| memory copy once the handshake is done. Implementations of this object must be |
| thread compatible. This object depends on grpc and the details of this object |
| is defined in transport_security_grpc.h. */ |
| |
| typedef struct tsi_zero_copy_grpc_protector tsi_zero_copy_grpc_protector; |
| |
| /* --- tsi_frame_protector object --- |
| |
| This object protects and unprotects buffers once the handshake is done. |
| Implementations of this object must be thread compatible. */ |
| |
| typedef struct tsi_frame_protector tsi_frame_protector; |
| |
| /* Outputs protected frames. |
| - unprotected_bytes is an input only parameter and points to the data |
| to be protected. |
| - unprotected_bytes_size is an input/output parameter used by the caller to |
| specify how many bytes are available in unprotected_bytes. The output |
| value is the number of bytes consumed during the call. |
| - protected_output_frames points to a buffer allocated by the caller that |
| will be written. |
| - protected_output_frames_size is an input/output parameter used by the |
| caller to specify how many bytes are available in protected_output_frames. |
| As an output, this value indicates the number of bytes written. |
| - This method returns TSI_OK in case of success or a specific error code in |
| case of failure. Note that even if all the input unprotected bytes are |
| consumed, they may not have been processed into the returned protected |
| output frames. The caller should call the protect_flush method |
| to make sure that there are no more protected bytes buffered in the |
| protector. |
| |
| A typical way to call this method would be: |
| |
| ------------------------------------------------------------------------ |
| unsigned char protected_buffer[4096]; |
| size_t protected_buffer_size = sizeof(protected_buffer); |
| tsi_result result = TSI_OK; |
| while (message_size > 0) { |
| size_t protected_buffer_size_to_send = protected_buffer_size; |
| size_t processed_message_size = message_size; |
| result = tsi_frame_protector_protect(protector, |
| message_bytes, |
| &processed_message_size, |
| protected_buffer, |
| &protected_buffer_size_to_send); |
| if (result != TSI_OK) break; |
| send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send); |
| message_bytes += processed_message_size; |
| message_size -= processed_message_size; |
| |
| // Don't forget to flush. |
| if (message_size == 0) { |
| size_t still_pending_size; |
| do { |
| protected_buffer_size_to_send = protected_buffer_size; |
| result = tsi_frame_protector_protect_flush( |
| protector, protected_buffer, |
| &protected_buffer_size_to_send, &still_pending_size); |
| if (result != TSI_OK) break; |
| send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send); |
| } while (still_pending_size > 0); |
| } |
| } |
| |
| if (result != TSI_OK) HandleError(result); |
| ------------------------------------------------------------------------ */ |
| tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, |
| const unsigned char* unprotected_bytes, |
| size_t* unprotected_bytes_size, |
| unsigned char* protected_output_frames, |
| size_t* protected_output_frames_size); |
| |
| /* Indicates that we need to flush the bytes buffered in the protector and get |
| the resulting frame. |
| - protected_output_frames points to a buffer allocated by the caller that |
| will be written. |
| - protected_output_frames_size is an input/output parameter used by the |
| caller to specify how many bytes are available in protected_output_frames. |
| - still_pending_bytes is an output parameter indicating the number of bytes |
| that still need to be flushed from the protector.*/ |
| tsi_result tsi_frame_protector_protect_flush( |
| tsi_frame_protector* self, unsigned char* protected_output_frames, |
| size_t* protected_output_frames_size, size_t* still_pending_size); |
| |
| /* Outputs unprotected bytes. |
| - protected_frames_bytes is an input only parameter and points to the |
| protected frames to be unprotected. |
| - protected_frames_bytes_size is an input/output only parameter used by the |
| caller to specify how many bytes are available in protected_bytes. The |
| output value is the number of bytes consumed during the call. |
| Implementations will buffer up to a frame of protected data. |
| - unprotected_bytes points to a buffer allocated by the caller that will be |
| written. |
| - unprotected_bytes_size is an input/output parameter used by the caller to |
| specify how many bytes are available in unprotected_bytes. This |
| value is expected to be at most max_protected_frame_size minus overhead |
| which means that max_protected_frame_size is a safe bet. The output value |
| is the number of bytes actually written. |
| If *unprotected_bytes_size is unchanged, there may be more data remaining |
| to unprotect, and the caller should call this function again. |
| |
| - This method returns TSI_OK in case of success. Success includes cases where |
| there is not enough data to output a frame in which case |
| unprotected_bytes_size will be set to 0 and cases where the internal buffer |
| needs to be read before new protected data can be processed in which case |
| protected_frames_size will be set to 0. */ |
| tsi_result tsi_frame_protector_unprotect( |
| tsi_frame_protector* self, const unsigned char* protected_frames_bytes, |
| size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, |
| size_t* unprotected_bytes_size); |
| |
| /* Destroys the tsi_frame_protector object. */ |
| void tsi_frame_protector_destroy(tsi_frame_protector* self); |
| |
| /* --- tsi_peer objects --- |
| |
| tsi_peer objects are a set of properties. The peer owns the properties. */ |
| |
| /* This property is of type TSI_PEER_PROPERTY_STRING. */ |
| #define TSI_CERTIFICATE_TYPE_PEER_PROPERTY "certificate_type" |
| |
| /* This property represents security level of a channel. */ |
| #define TSI_SECURITY_LEVEL_PEER_PROPERTY "security_level" |
| |
| /* Property values may contain NULL characters just like C++ strings. |
| The length field gives the length of the string. */ |
| typedef struct tsi_peer_property { |
| char* name; |
| struct { |
| char* data; |
| size_t length; |
| } value; |
| } tsi_peer_property; |
| |
| struct tsi_peer { |
| tsi_peer_property* properties; |
| size_t property_count; |
| }; |
| /* Destructs the tsi_peer object. */ |
| void tsi_peer_destruct(tsi_peer* self); |
| |
| /* --- tsi_handshaker_result object --- |
| |
| This object contains all necessary handshake results and data such as peer |
| info, negotiated keys, unused handshake bytes, when the handshake completes. |
| Implementations of this object must be thread compatible. */ |
| |
| typedef struct tsi_handshaker_result tsi_handshaker_result; |
| |
| /* This method extracts tsi peer. It returns TSI_OK assuming there is no fatal |
| error. |
| The caller is responsible for destructing the peer. */ |
| tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result* self, |
| tsi_peer* peer); |
| |
| /* This method indicates what type of frame protector is provided by the |
| TSI implementation. */ |
| tsi_result tsi_handshaker_result_get_frame_protector_type( |
| const tsi_handshaker_result* self, |
| tsi_frame_protector_type* frame_protector_type); |
| |
| /* This method creates a tsi_frame_protector object. It returns TSI_OK assuming |
| there is no fatal error. |
| The caller is responsible for destroying the protector. */ |
| tsi_result tsi_handshaker_result_create_frame_protector( |
| const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, |
| tsi_frame_protector** protector); |
| |
| /* This method returns the unused bytes from the handshake. It returns TSI_OK |
| assuming there is no fatal error. |
| Ownership of the bytes is retained by the handshaker result. As a |
| consequence, the caller must not free the bytes. */ |
| tsi_result tsi_handshaker_result_get_unused_bytes( |
| const tsi_handshaker_result* self, const unsigned char** bytes, |
| size_t* bytes_size); |
| |
| /* This method releases the tsi_handshaker_handshaker object. After this method |
| is called, no other method can be called on the object. */ |
| void tsi_handshaker_result_destroy(tsi_handshaker_result* self); |
| |
| /* --- tsi_handshaker objects ---- |
| |
| Implementations of this object must be thread compatible. |
| |
| ------------------------------------------------------------------------ |
| |
| A typical usage supporting both synchronous and asynchronous TSI handshaker |
| implementations would be: |
| |
| ------------------------------------------------------------------------ |
| |
| typedef struct { |
| tsi_handshaker *handshaker; |
| tsi_handshaker_result *handshaker_result; |
| unsigned char *handshake_buffer; |
| size_t handshake_buffer_size; |
| ... |
| } security_handshaker; |
| |
| void do_handshake(security_handshaker *h, ...) { |
| // Start the handshake by the calling do_handshake_next. |
| do_handshake_next(h, NULL, 0); |
| ... |
| } |
| |
| // This method is the callback function when data is received from the |
| // peer. This method will read bytes into the handshake buffer and call |
| // do_handshake_next. |
| void on_handshake_data_received_from_peer(void *user_data) { |
| security_handshaker *h = (security_handshaker *)user_data; |
| size_t bytes_received_size = h->handshake_buffer_size; |
| read_bytes_from_peer(h->handshake_buffer, &bytes_received_size); |
| do_handshake_next(h, h->handshake_buffer, bytes_received_size); |
| } |
| |
| // This method processes a step of handshake, calling tsi_handshaker_next. |
| void do_handshake_next(security_handshaker *h, |
| const unsigned char* bytes_received, |
| size_t bytes_received_size) { |
| tsi_result status = TSI_OK; |
| unsigned char *bytes_to_send = NULL; |
| size_t bytes_to_send_size = 0; |
| tsi_handshaker_result *result = NULL; |
| status = tsi_handshaker_next( |
| handshaker, bytes_received, bytes_received_size, &bytes_to_send, |
| &bytes_to_send_size, &result, on_handshake_next_done, h); |
| // If TSI handshaker is asynchronous, on_handshake_next_done will be |
| // executed inside tsi_handshaker_next. |
| if (status == TSI_ASYNC) return; |
| // If TSI handshaker is synchronous, invoke callback directly in this |
| // thread. |
| on_handshake_next_done(status, (void *)h, bytes_to_send, |
| bytes_to_send_size, result); |
| } |
| |
| // This is the callback function to execute after tsi_handshaker_next. |
| // It is passed to tsi_handshaker_next as a function parameter. |
| void on_handshake_next_done( |
| tsi_result status, void *user_data, const unsigned char *bytes_to_send, |
| size_t bytes_to_send_size, tsi_handshaker_result *result) { |
| security_handshaker *h = (security_handshaker *)user_data; |
| if (status == TSI_INCOMPLETE_DATA) { |
| // Schedule an asynchronous read from the peer. If handshake data are |
| // received, on_handshake_data_received_from_peer will be called. |
| async_read_from_peer(..., ..., on_handshake_data_received_from_peer); |
| return; |
| } |
| if (status != TSI_OK) return; |
| |
| if (bytes_to_send_size > 0) { |
| send_bytes_to_peer(bytes_to_send, bytes_to_send_size); |
| } |
| |
| if (result != NULL) { |
| // Handshake completed. |
| h->result = result; |
| // Check the Peer. |
| tsi_peer peer; |
| status = tsi_handshaker_result_extract_peer(result, &peer); |
| if (status != TSI_OK) return; |
| status = check_peer(&peer); |
| tsi_peer_destruct(&peer); |
| if (status != TSI_OK) return; |
| |
| // Create the protector. |
| tsi_frame_protector* protector = NULL; |
| status = tsi_handshaker_result_create_frame_protector(result, NULL, |
| &protector); |
| if (status != TSI_OK) return; |
| |
| // Do not forget to unprotect outstanding data if any. |
| .... |
| } |
| } |
| ------------------------------------------------------------------------ */ |
| typedef struct tsi_handshaker tsi_handshaker; |
| |
| /* TODO(jiangtaoli2016): Cleans up deprecated methods when we are ready. */ |
| |
| /* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead. |
| Gets bytes that need to be sent to the peer. |
| - bytes is the buffer that will be written with the data to be sent to the |
| peer. |
| - bytes_size is an input/output parameter specifying the capacity of the |
| bytes parameter as input and the number of bytes written as output. |
| Returns TSI_OK if all the data to send to the peer has been written or if |
| nothing has to be sent to the peer (in which base bytes_size outputs to 0), |
| otherwise returns TSI_INCOMPLETE_DATA which indicates that this method |
| needs to be called again to get all the bytes to send to the peer (there |
| was more data to write than the specified bytes_size). In case of a fatal |
| error in the handshake, another specific error code is returned. */ |
| tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, |
| unsigned char* bytes, |
| size_t* bytes_size); |
| |
| /* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead. |
| Processes bytes received from the peer. |
| - bytes is the buffer containing the data. |
| - bytes_size is an input/output parameter specifying the size of the data as |
| input and the number of bytes consumed as output. |
| Return TSI_OK if the handshake has all the data it needs to process, |
| otherwise return TSI_INCOMPLETE_DATA which indicates that this method |
| needs to be called again to complete the data needed for processing. In |
| case of a fatal error in the handshake, another specific error code is |
| returned. */ |
| tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self, |
| const unsigned char* bytes, |
| size_t* bytes_size); |
| |
| /* TO BE DEPRECATED SOON. |
| Gets the result of the handshaker. |
| Returns TSI_OK if the hanshake completed successfully and there has been no |
| errors. Returns TSI_HANDSHAKE_IN_PROGRESS if the handshaker is not done yet |
| but no error has been encountered so far. Otherwise the handshaker failed |
| with the returned error. */ |
| tsi_result tsi_handshaker_get_result(tsi_handshaker* self); |
| |
| /* TO BE DEPRECATED SOON. |
| Returns 1 if the handshake is in progress, 0 otherwise. */ |
| #define tsi_handshaker_is_in_progress(h) \ |
| (tsi_handshaker_get_result((h)) == TSI_HANDSHAKE_IN_PROGRESS) |
| |
| /* TO BE DEPRECATED SOON. Use tsi_handshaker_result_extract_peer instead. |
| This method may return TSI_FAILED_PRECONDITION if |
| tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise |
| assuming the handshaker is not in a fatal error state. |
| The caller is responsible for destructing the peer. */ |
| tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer); |
| |
| /* TO BE DEPRECATED SOON. Use tsi_handshaker_result_create_frame_protector |
| instead. |
| This method creates a tsi_frame_protector object after the handshake phase |
| is done. After this method has been called successfully, the only method |
| that can be called on this object is Destroy. |
| - max_output_protected_frame_size is an input/output parameter specifying the |
| desired max output protected frame size as input and outputing the actual |
| max output frame size as the output. Passing NULL is OK and will result in |
| the implementation choosing the default maximum protected frame size. Note |
| that this size only applies to outgoing frames (generated with |
| tsi_frame_protector_protect) and not incoming frames (input of |
| tsi_frame_protector_unprotect). |
| - protector is an output parameter pointing to the newly created |
| tsi_frame_protector object. |
| This method may return TSI_FAILED_PRECONDITION if |
| tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise assuming |
| the handshaker is not in a fatal error state. |
| The caller is responsible for destroying the protector. */ |
| tsi_result tsi_handshaker_create_frame_protector( |
| tsi_handshaker* self, size_t* max_output_protected_frame_size, |
| tsi_frame_protector** protector); |
| |
| /* Callback function definition for tsi_handshaker_next. |
| - status indicates the status of the next operation. |
| - user_data is the argument to callback function passed from the caller. |
| - bytes_to_send is the data buffer to be sent to the peer. |
| - bytes_to_send_size is the size of data buffer to be sent to the peer. |
| - handshaker_result is the result of handshake when the handshake completes, |
| is NULL otherwise. */ |
| typedef void (*tsi_handshaker_on_next_done_cb)( |
| tsi_result status, void* user_data, const unsigned char* bytes_to_send, |
| size_t bytes_to_send_size, tsi_handshaker_result* handshaker_result); |
| |
| /* Conduct a next step of the handshake. |
| - received_bytes is the buffer containing the data received from the peer. |
| - received_bytes_size is the size of the data received from the peer. |
| - bytes_to_send is the data buffer to be sent to the peer. |
| - bytes_to_send_size is the size of data buffer to be sent to the peer. |
| - handshaker_result is the result of handshake if the handshake completes. |
| - cb is the callback function defined above. It can be NULL for synchronous |
| TSI handshaker implementation. |
| - user_data is the argument to callback function passed from the caller. |
| This method returns TSI_ASYNC if the TSI handshaker implementation is |
| asynchronous, and in this case, the callback is guaranteed to run in another |
| thread owned by TSI. It returns TSI_OK if the handshake completes or if |
| there are data to send to the peer, otherwise returns TSI_INCOMPLETE_DATA |
| which indicates that this method needs to be called again with more data |
| from the peer. In case of a fatal error in the handshake, another specific |
| error code is returned. |
| The caller is responsible for destroying the handshaker_result. However, |
| the caller should not free bytes_to_send, as the buffer is owned by the |
| tsi_handshaker object. */ |
| tsi_result tsi_handshaker_next( |
| tsi_handshaker* self, const unsigned char* received_bytes, |
| size_t received_bytes_size, const unsigned char** bytes_to_send, |
| size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result, |
| tsi_handshaker_on_next_done_cb cb, void* user_data); |
| |
| /* This method shuts down a TSI handshake that is in progress. |
| * |
| * This method will be invoked when TSI handshake should be terminated before |
| * being finished in order to free any resources being used. |
| */ |
| void tsi_handshaker_shutdown(tsi_handshaker* self); |
| |
| /* This method releases the tsi_handshaker object. After this method is called, |
| no other method can be called on the object. */ |
| void tsi_handshaker_destroy(tsi_handshaker* self); |
| |
| /* This method initializes the necessary shared objects used for tsi |
| implementation. */ |
| void tsi_init(); |
| |
| /* This method destroys the shared objects created by tsi_init. */ |
| void tsi_destroy(); |
| |
| #endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H */ |