// // // 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_SRC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H #define GRPC_SRC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H #include <grpc/support/port_platform.h> #include <stdint.h> #include <stdlib.h> #include <string> #include "src/core/lib/debug/trace.h" // --- tsi result --- tsi_result; tsi_security_level; tsi_client_certificate_request_type; tsi_frame_protector_type; 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. 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. 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 … // This property represents security level of a channel. #define TSI_SECURITY_LEVEL_PEER_PROPERTY … // Property values may contain NULL characters just like C++ strings. // The length field gives the length of the string. tsi_peer_property; struct tsi_peer { … }; // 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. 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. // .... // } // } // ------------------------------------------------------------------------ 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) … // 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. tsi_handshaker_on_next_done_cb; // 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. // - error, if non-null, will be populated with a human-readable error // message whenever the result value is something other than TSI_OK, // TSI_ASYNC, or TSI_INCOMPLETE_DATA. The object pointed to by this // argument is owned by the caller and must continue to exist until after the // handshake is finished. Some TSI implementations cache this value, // so callers must pass the same value to all calls to tsi_handshaker_next() // for a given handshake. // 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, std::string* error = nullptr); // 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_SRC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H