// // // 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. // // #include "src/core/tsi/transport_security.h" #include #include #include #include #include // --- tsi_result common implementation. --- const char* tsi_result_to_string(tsi_result result) { switch (result) { case TSI_OK: return "TSI_OK"; case TSI_UNKNOWN_ERROR: return "TSI_UNKNOWN_ERROR"; case TSI_INVALID_ARGUMENT: return "TSI_INVALID_ARGUMENT"; case TSI_PERMISSION_DENIED: return "TSI_PERMISSION_DENIED"; case TSI_INCOMPLETE_DATA: return "TSI_INCOMPLETE_DATA"; case TSI_FAILED_PRECONDITION: return "TSI_FAILED_PRECONDITION"; case TSI_UNIMPLEMENTED: return "TSI_UNIMPLEMENTED"; case TSI_INTERNAL_ERROR: return "TSI_INTERNAL_ERROR"; case TSI_DATA_CORRUPTED: return "TSI_DATA_CORRUPTED"; case TSI_NOT_FOUND: return "TSI_NOT_FOUND"; case TSI_PROTOCOL_FAILURE: return "TSI_PROTOCOL_FAILURE"; case TSI_HANDSHAKE_IN_PROGRESS: return "TSI_HANDSHAKE_IN_PROGRESS"; case TSI_OUT_OF_RESOURCES: return "TSI_OUT_OF_RESOURCES"; case TSI_ASYNC: return "TSI_ASYNC"; default: return "UNKNOWN"; } } const char* tsi_security_level_to_string(tsi_security_level security_level) { switch (security_level) { case TSI_SECURITY_NONE: return "TSI_SECURITY_NONE"; case TSI_INTEGRITY_ONLY: return "TSI_INTEGRITY_ONLY"; case TSI_PRIVACY_AND_INTEGRITY: return "TSI_PRIVACY_AND_INTEGRITY"; default: return "UNKNOWN"; } } // --- tsi_frame_protector common implementation. --- // Calls specific implementation after state/input validation. 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) { if (self == nullptr || self->vtable == nullptr || unprotected_bytes == nullptr || unprotected_bytes_size == nullptr || protected_output_frames == nullptr || protected_output_frames_size == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->vtable->protect == nullptr) return TSI_UNIMPLEMENTED; return self->vtable->protect(self, unprotected_bytes, unprotected_bytes_size, protected_output_frames, protected_output_frames_size); } 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) { if (self == nullptr || self->vtable == nullptr || protected_output_frames == nullptr || protected_output_frames_size == nullptr || still_pending_size == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->vtable->protect_flush == nullptr) return TSI_UNIMPLEMENTED; return self->vtable->protect_flush(self, protected_output_frames, protected_output_frames_size, still_pending_size); } 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) { if (self == nullptr || self->vtable == nullptr || protected_frames_bytes == nullptr || protected_frames_bytes_size == nullptr || unprotected_bytes == nullptr || unprotected_bytes_size == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->vtable->unprotect == nullptr) return TSI_UNIMPLEMENTED; return self->vtable->unprotect(self, protected_frames_bytes, protected_frames_bytes_size, unprotected_bytes, unprotected_bytes_size); } void tsi_frame_protector_destroy(tsi_frame_protector* self) { if (self == nullptr) return; self->vtable->destroy(self); } // --- tsi_handshaker common implementation. --- // Calls specific implementation after state/input validation. tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, unsigned char* bytes, size_t* bytes_size) { if (self == nullptr || self->vtable == nullptr || bytes == nullptr || bytes_size == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; if (self->vtable->get_bytes_to_send_to_peer == nullptr) { return TSI_UNIMPLEMENTED; } return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size); } tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) { if (self == nullptr || self->vtable == nullptr || bytes == nullptr || bytes_size == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; if (self->vtable->process_bytes_from_peer == nullptr) { return TSI_UNIMPLEMENTED; } return self->vtable->process_bytes_from_peer(self, bytes, bytes_size); } tsi_result tsi_handshaker_get_result(tsi_handshaker* self) { if (self == nullptr || self->vtable == nullptr) return TSI_INVALID_ARGUMENT; if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; if (self->vtable->get_result == nullptr) return TSI_UNIMPLEMENTED; return self->vtable->get_result(self); } tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer) { if (self == nullptr || self->vtable == nullptr || peer == nullptr) { return TSI_INVALID_ARGUMENT; } memset(peer, 0, sizeof(tsi_peer)); if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; if (tsi_handshaker_get_result(self) != TSI_OK) { return TSI_FAILED_PRECONDITION; } if (self->vtable->extract_peer == nullptr) return TSI_UNIMPLEMENTED; return self->vtable->extract_peer(self, peer); } tsi_result tsi_handshaker_create_frame_protector( tsi_handshaker* self, size_t* max_output_protected_frame_size, tsi_frame_protector** protector) { tsi_result result; if (self == nullptr || self->vtable == nullptr || protector == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; if (tsi_handshaker_get_result(self) != TSI_OK) return TSI_FAILED_PRECONDITION; if (self->vtable->create_frame_protector == nullptr) return TSI_UNIMPLEMENTED; result = self->vtable->create_frame_protector( self, max_output_protected_frame_size, protector); if (result == TSI_OK) { self->frame_protector_created = true; } return result; } 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) { if (self == nullptr || self->vtable == nullptr) { if (error != nullptr) *error = "invalid argument"; return TSI_INVALID_ARGUMENT; } if (self->handshaker_result_created) { if (error != nullptr) *error = "handshaker already returned a result"; return TSI_FAILED_PRECONDITION; } if (self->handshake_shutdown) { if (error != nullptr) *error = "handshaker shutdown"; return TSI_HANDSHAKE_SHUTDOWN; } if (self->vtable->next == nullptr) { if (error != nullptr) *error = "TSI handshaker does not implement next()"; return TSI_UNIMPLEMENTED; } return self->vtable->next(self, received_bytes, received_bytes_size, bytes_to_send, bytes_to_send_size, handshaker_result, cb, user_data, error); } void tsi_handshaker_shutdown(tsi_handshaker* self) { if (self == nullptr || self->vtable == nullptr) return; if (self->vtable->shutdown != nullptr) { self->vtable->shutdown(self); } self->handshake_shutdown = true; } void tsi_handshaker_destroy(tsi_handshaker* self) { if (self == nullptr) return; self->vtable->destroy(self); } // --- tsi_handshaker_result implementation. --- tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result* self, tsi_peer* peer) { if (self == nullptr || self->vtable == nullptr || peer == nullptr) { return TSI_INVALID_ARGUMENT; } memset(peer, 0, sizeof(tsi_peer)); if (self->vtable->extract_peer == nullptr) return TSI_UNIMPLEMENTED; return self->vtable->extract_peer(self, peer); } tsi_result tsi_handshaker_result_get_frame_protector_type( const tsi_handshaker_result* self, tsi_frame_protector_type* frame_protector_type) { if (self == nullptr || frame_protector_type == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->vtable->get_frame_protector_type == nullptr) { return TSI_UNIMPLEMENTED; } return self->vtable->get_frame_protector_type(self, frame_protector_type); } tsi_result tsi_handshaker_result_create_frame_protector( const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, tsi_frame_protector** protector) { if (self == nullptr || self->vtable == nullptr || protector == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->vtable->create_frame_protector == nullptr) return TSI_UNIMPLEMENTED; return self->vtable->create_frame_protector( self, max_output_protected_frame_size, protector); } tsi_result tsi_handshaker_result_get_unused_bytes( const tsi_handshaker_result* self, const unsigned char** bytes, size_t* bytes_size) { if (self == nullptr || self->vtable == nullptr || bytes == nullptr || bytes_size == nullptr) { return TSI_INVALID_ARGUMENT; } if (self->vtable->get_unused_bytes == nullptr) return TSI_UNIMPLEMENTED; return self->vtable->get_unused_bytes(self, bytes, bytes_size); } void tsi_handshaker_result_destroy(tsi_handshaker_result* self) { if (self == nullptr) return; self->vtable->destroy(self); } // --- tsi_peer implementation. --- tsi_peer_property tsi_init_peer_property(void) { tsi_peer_property property; memset(&property, 0, sizeof(tsi_peer_property)); return property; } static void tsi_peer_destroy_list_property(tsi_peer_property* children, size_t child_count) { size_t i; for (i = 0; i < child_count; i++) { tsi_peer_property_destruct(&children[i]); } gpr_free(children); } void tsi_peer_property_destruct(tsi_peer_property* property) { if (property->name != nullptr) { gpr_free(property->name); } if (property->value.data != nullptr) { gpr_free(property->value.data); } *property = tsi_init_peer_property(); // Reset everything to 0. } void tsi_peer_destruct(tsi_peer* self) { if (self == nullptr) return; if (self->properties != nullptr) { tsi_peer_destroy_list_property(self->properties, self->property_count); self->properties = nullptr; } self->property_count = 0; } tsi_result tsi_construct_allocated_string_peer_property( const char* name, size_t value_length, tsi_peer_property* property) { *property = tsi_init_peer_property(); if (name != nullptr) property->name = gpr_strdup(name); if (value_length > 0) { property->value.data = static_cast(gpr_zalloc(value_length)); property->value.length = value_length; } return TSI_OK; } tsi_result tsi_construct_string_peer_property_from_cstring( const char* name, const char* value, tsi_peer_property* property) { return tsi_construct_string_peer_property(name, value, strlen(value), property); } tsi_result tsi_construct_string_peer_property(const char* name, const char* value, size_t value_length, tsi_peer_property* property) { tsi_result result = tsi_construct_allocated_string_peer_property( name, value_length, property); if (result != TSI_OK) return result; if (value_length > 0) { memcpy(property->value.data, value, value_length); } return TSI_OK; } tsi_result tsi_construct_peer(size_t property_count, tsi_peer* peer) { memset(peer, 0, sizeof(tsi_peer)); if (property_count > 0) { peer->properties = static_cast( gpr_zalloc(property_count * sizeof(tsi_peer_property))); peer->property_count = property_count; } return TSI_OK; } const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* peer, const char* name) { size_t i; if (peer == nullptr) return nullptr; for (i = 0; i < peer->property_count; i++) { const tsi_peer_property* property = &peer->properties[i]; if (name == nullptr && property->name == nullptr) { return property; } if (name != nullptr && property->name != nullptr && strcmp(property->name, name) == 0) { return property; } } return nullptr; }