| /* |
| * |
| * 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 <grpc/support/alloc.h> |
| #include <grpc/support/string_util.h> |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| /* --- Tracing. --- */ |
| |
| grpc_tracer_flag tsi_tracing_enabled = GRPC_TRACER_INITIALIZER(false, "tsi"); |
| |
| /* --- 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"; |
| } |
| } |
| |
| /* --- 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 == NULL || self->vtable == NULL || unprotected_bytes == NULL || |
| unprotected_bytes_size == NULL || protected_output_frames == NULL || |
| protected_output_frames_size == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| if (self->vtable->protect == NULL) 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 == NULL || self->vtable == NULL || protected_output_frames == NULL || |
| protected_output_frames_size == NULL || still_pending_size == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| if (self->vtable->protect_flush == NULL) 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 == NULL || self->vtable == NULL || protected_frames_bytes == NULL || |
| protected_frames_bytes_size == NULL || unprotected_bytes == NULL || |
| unprotected_bytes_size == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| if (self->vtable->unprotect == NULL) 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 == NULL) 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 == NULL || self->vtable == NULL || bytes == NULL || |
| bytes_size == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; |
| if (self->vtable->get_bytes_to_send_to_peer == NULL) 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 == NULL || self->vtable == NULL || bytes == NULL || |
| bytes_size == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; |
| if (self->vtable->process_bytes_from_peer == NULL) 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 == NULL || self->vtable == NULL) return TSI_INVALID_ARGUMENT; |
| if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; |
| if (self->vtable->get_result == NULL) return TSI_UNIMPLEMENTED; |
| return self->vtable->get_result(self); |
| } |
| |
| tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer) { |
| if (self == NULL || self->vtable == NULL || peer == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| memset(peer, 0, sizeof(tsi_peer)); |
| if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; |
| if (tsi_handshaker_get_result(self) != TSI_OK) { |
| return TSI_FAILED_PRECONDITION; |
| } |
| if (self->vtable->extract_peer == NULL) return TSI_UNIMPLEMENTED; |
| return self->vtable->extract_peer(self, peer); |
| } |
| |
| tsi_result tsi_handshaker_create_frame_protector( |
| tsi_handshaker *self, size_t *max_protected_frame_size, |
| tsi_frame_protector **protector) { |
| tsi_result result; |
| if (self == NULL || self->vtable == NULL || protector == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; |
| if (tsi_handshaker_get_result(self) != TSI_OK) return TSI_FAILED_PRECONDITION; |
| if (self->vtable->create_frame_protector == NULL) return TSI_UNIMPLEMENTED; |
| result = self->vtable->create_frame_protector(self, max_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) { |
| if (self == NULL || self->vtable == NULL) return TSI_INVALID_ARGUMENT; |
| if (self->handshaker_result_created) return TSI_FAILED_PRECONDITION; |
| if (self->vtable->next == NULL) 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); |
| } |
| |
| void tsi_handshaker_destroy(tsi_handshaker *self) { |
| if (self == NULL) 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 == NULL || self->vtable == NULL || peer == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| memset(peer, 0, sizeof(tsi_peer)); |
| if (self->vtable->extract_peer == NULL) return TSI_UNIMPLEMENTED; |
| return self->vtable->extract_peer(self, peer); |
| } |
| |
| tsi_result tsi_handshaker_result_create_frame_protector( |
| const tsi_handshaker_result *self, size_t *max_protected_frame_size, |
| tsi_frame_protector **protector) { |
| if (self == NULL || self->vtable == NULL || protector == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| if (self->vtable->create_frame_protector == NULL) return TSI_UNIMPLEMENTED; |
| return self->vtable->create_frame_protector(self, max_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 == NULL || self->vtable == NULL || bytes == NULL || |
| bytes_size == NULL) { |
| return TSI_INVALID_ARGUMENT; |
| } |
| if (self->vtable->get_unused_bytes == NULL) return TSI_UNIMPLEMENTED; |
| return self->vtable->get_unused_bytes(self, bytes, bytes_size); |
| } |
| |
| void tsi_handshaker_result_destroy(tsi_handshaker_result *self) { |
| if (self == NULL) 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 != NULL) { |
| gpr_free(property->name); |
| } |
| if (property->value.data != NULL) { |
| gpr_free(property->value.data); |
| } |
| *property = tsi_init_peer_property(); /* Reset everything to 0. */ |
| } |
| |
| void tsi_peer_destruct(tsi_peer *self) { |
| if (self == NULL) return; |
| if (self->properties != NULL) { |
| tsi_peer_destroy_list_property(self->properties, self->property_count); |
| self->properties = NULL; |
| } |
| 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 != NULL) property->name = gpr_strdup(name); |
| if (value_length > 0) { |
| property->value.data = 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 = gpr_zalloc(property_count * sizeof(tsi_peer_property)); |
| peer->property_count = property_count; |
| } |
| return TSI_OK; |
| } |